OSDN Git Service

am 0fbe1dfb: Merge "cherrypick from master: Change-Id: I169749dc594ca1d79a802db4c53ec...
authorJoe Fernandez <joefernandez@google.com>
Fri, 8 Jul 2011 16:39:22 +0000 (09:39 -0700)
committerAndroid Git Automerger <android-git-automerger@android.com>
Fri, 8 Jul 2011 16:39:22 +0000 (09:39 -0700)
* commit '0fbe1dfb8df94a51d79e9a42fcbaebed6f6e516e':
  cherrypick from master: Change-Id: I169749dc594ca1d79a802db4c53ec330924fdd2c

143 files changed:
Android.mk
CleanSpec.mk
cmds/dumpstate/dumpstate.c
core/java/android/app/Notification.java
core/java/android/bluetooth/BluetoothDeviceProfileState.java
core/java/android/bluetooth/BluetoothProfileState.java
core/java/android/bluetooth/BluetoothTetheringDataTracker.java
core/java/android/net/ConnectivityManager.java
core/java/android/net/DhcpInfoInternal.java
core/java/android/net/DhcpStateMachine.java
core/java/android/net/DummyDataStateTracker.java
core/java/android/net/EthernetDataTracker.java
core/java/android/net/IConnectivityManager.aidl
core/java/android/net/LinkAddress.java
core/java/android/net/LinkProperties.java
core/java/android/net/MobileDataStateTracker.java
core/java/android/net/NetworkConfig.java [new file with mode: 0644]
core/java/android/net/NetworkStateTracker.java
core/java/android/net/NetworkUtils.java
core/java/android/net/ProxyProperties.java
core/java/android/net/RouteInfo.aidl [new file with mode: 0644]
core/java/android/net/RouteInfo.java [new file with mode: 0644]
core/java/android/nfc/INfcAdapter.aidl
core/java/android/nfc/INfcAdapterExtras.aidl [moved from core/java/android/nfc/INfcSecureElement.aidl with 63% similarity]
core/java/android/nfc/NfcAdapter.java
core/java/android/nfc/NfcSecureElement.java [deleted file]
core/java/android/os/FileUtils.java
core/java/android/os/INetworkManagementService.aidl
core/java/android/provider/Settings.java
core/java/android/provider/Telephony.java
core/java/android/view/ViewRoot.java
core/java/com/android/internal/util/AsyncChannel.java
core/java/com/android/internal/util/IState.java [moved from core/java/com/android/internal/util/HierarchicalState.java with 60% similarity]
core/java/com/android/internal/util/ProcessedMessages.java [deleted file]
core/java/com/android/internal/util/Protocol.java
core/java/com/android/internal/util/State.java [new file with mode: 0644]
core/java/com/android/internal/util/StateMachine.java [moved from core/java/com/android/internal/util/HierarchicalStateMachine.java with 76% similarity]
core/java/com/android/internal/widget/LockPatternUtils.java
core/java/com/google/android/mms/pdu/PduParser.java [changed mode: 0644->0755]
core/java/com/google/android/mms/util/PduCache.java
core/jni/android_net_NetUtils.cpp
core/jni/android_util_Binder.cpp
core/res/AndroidManifest.xml
core/res/res/layout/keyguard_screen_sim_puk_landscape.xml [new file with mode: 0644]
core/res/res/layout/keyguard_screen_sim_puk_portrait.xml [new file with mode: 0644]
core/res/res/values/config.xml [changed mode: 0644->0755]
core/res/res/values/strings.xml
core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
core/tests/coretests/src/android/net/LinkPropertiesTest.java [new file with mode: 0644]
core/tests/coretests/src/com/android/internal/util/StateMachineTest.java [moved from core/tests/coretests/src/com/android/internal/util/HierarchicalStateMachineTest.java with 84% similarity]
data/etc/platform.xml
media/libstagefright/HTTPStream.cpp
media/tests/omxjpegdecoder/Android.mk
nfc-extras/Android.mk [new file with mode: 0644]
nfc-extras/com.android.nfc_extras.xml [new file with mode: 0644]
nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java [new file with mode: 0644]
nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java [new file with mode: 0644]
packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4g.png [new file with mode: 0644]
packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
policy/src/com/android/internal/policy/impl/LockPatternKeyguardViewProperties.java
policy/src/com/android/internal/policy/impl/LockScreen.java
policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java [new file with mode: 0644]
policy/src/com/android/internal/policy/impl/SimUnlockScreen.java
services/java/com/android/server/ConnectivityService.java
services/java/com/android/server/NetworkManagementService.java
services/java/com/android/server/TelephonyRegistry.java
services/java/com/android/server/WifiService.java
services/java/com/android/server/connectivity/Tethering.java
telephony/java/android/telephony/ServiceState.java
telephony/java/android/telephony/SignalStrength.java
telephony/java/android/telephony/TelephonyManager.java
telephony/java/com/android/internal/telephony/ApnContext.java [new file with mode: 0644]
telephony/java/com/android/internal/telephony/ApnSetting.java
telephony/java/com/android/internal/telephony/BaseCommands.java
telephony/java/com/android/internal/telephony/CallTracker.java
telephony/java/com/android/internal/telephony/CommandsInterface.java
telephony/java/com/android/internal/telephony/DataCallState.java
telephony/java/com/android/internal/telephony/DataConnection.java
telephony/java/com/android/internal/telephony/DataConnectionAc.java [new file with mode: 0644]
telephony/java/com/android/internal/telephony/DataConnectionTracker.java
telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
telephony/java/com/android/internal/telephony/ITelephony.aidl
telephony/java/com/android/internal/telephony/IccCard.java
telephony/java/com/android/internal/telephony/IccCardApplication.java
telephony/java/com/android/internal/telephony/IccCardStatus.java
telephony/java/com/android/internal/telephony/IccConstants.java
telephony/java/com/android/internal/telephony/IccFileHandler.java
telephony/java/com/android/internal/telephony/IccRecords.java
telephony/java/com/android/internal/telephony/IccUtils.java
telephony/java/com/android/internal/telephony/MccTable.java
telephony/java/com/android/internal/telephony/Phone.java
telephony/java/com/android/internal/telephony/PhoneBase.java
telephony/java/com/android/internal/telephony/PhoneFactory.java
telephony/java/com/android/internal/telephony/PhoneProxy.java
telephony/java/com/android/internal/telephony/RIL.java
telephony/java/com/android/internal/telephony/RILConstants.java
telephony/java/com/android/internal/telephony/RestrictedState.java [moved from telephony/java/com/android/internal/telephony/gsm/RestrictedState.java with 98% similarity]
telephony/java/com/android/internal/telephony/RetryManager.java
telephony/java/com/android/internal/telephony/SMSDispatcher.java
telephony/java/com/android/internal/telephony/ServiceStateTracker.java
telephony/java/com/android/internal/telephony/TelephonyProperties.java
telephony/java/com/android/internal/telephony/cat/CatService.java
telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
telephony/java/com/android/internal/telephony/cat/ResponseData.java
telephony/java/com/android/internal/telephony/cat/RilMessageDecoder.java
telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java [new file with mode: 0644]
telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java
telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java [new file with mode: 0644]
telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java [new file with mode: 0644]
telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java [new file with mode: 0755]
telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
telephony/java/com/android/internal/telephony/cdma/RuimCard.java
telephony/java/com/android/internal/telephony/cdma/RuimPhoneBookInterfaceManager.java
telephony/java/com/android/internal/telephony/cdma/RuimRecords.java
telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java [changed mode: 0644->0755]
telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
telephony/java/com/android/internal/telephony/gsm/GsmConnection.java
telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java
telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
telephony/java/com/android/internal/telephony/gsm/SimCard.java
telephony/java/com/android/internal/telephony/gsm/SimPhoneBookInterfaceManager.java
telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java
telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
wifi/java/android/net/wifi/SupplicantStateTracker.java
wifi/java/android/net/wifi/WifiConfigStore.java
wifi/java/android/net/wifi/WifiStateMachine.java
wifi/java/android/net/wifi/WifiStateTracker.java
wifi/java/android/net/wifi/WpsStateMachine.java

index ca72a12..810c060 100644 (file)
@@ -123,10 +123,10 @@ LOCAL_SRC_FILES += \
        core/java/android/nfc/ILlcpServiceSocket.aidl \
        core/java/android/nfc/ILlcpSocket.aidl \
        core/java/android/nfc/INfcAdapter.aidl \
+       core/java/android/nfc/INfcAdapterExtras.aidl \
        core/java/android/nfc/INfcTag.aidl \
        core/java/android/nfc/IP2pInitiator.aidl \
        core/java/android/nfc/IP2pTarget.aidl \
-    core/java/android/nfc/INfcSecureElement.aidl \
        core/java/android/os/IHardwareService.aidl \
        core/java/android/os/IMessenger.aidl \
        core/java/android/os/INetworkManagementService.aidl \
index 2eee813..023ce59 100644 (file)
@@ -95,7 +95,7 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/ModelViewer_inte
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/PerfTest_intermediates/)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/RSTest_intermediates/)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/hardware/IUsbManager.java)
-
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/nfc)
 
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
index f6d702f..209417b 100644 (file)
@@ -47,9 +47,11 @@ static void dumpstate() {
     char build[PROPERTY_VALUE_MAX], fingerprint[PROPERTY_VALUE_MAX];
     char radio[PROPERTY_VALUE_MAX], bootloader[PROPERTY_VALUE_MAX];
     char network[PROPERTY_VALUE_MAX], date[80];
+    char build_type[PROPERTY_VALUE_MAX];
 
     property_get("ro.build.display.id", build, "(unknown)");
     property_get("ro.build.fingerprint", fingerprint, "(unknown)");
+    property_get("ro.build.type", build_type, "(unknown)");
     property_get("ro.baseband", radio, "(unknown)");
     property_get("ro.bootloader", bootloader, "(unknown)");
     property_get("gsm.operator.alpha", network, "(unknown)");
@@ -109,7 +111,10 @@ static void dumpstate() {
 
     run_command("NETWORK INTERFACES", 10, "su", "root", "netcfg", NULL);
     dump_file("NETWORK ROUTES", "/proc/net/route");
+    dump_file("NETWORK ROUTES IPV6", "/proc/net/ipv6_route");
     dump_file("ARP CACHE", "/proc/net/arp");
+    run_command("IPTABLES", 10, "su", "root", "iptables", "-L", NULL);
+    run_command("IPTABLE NAT", 10, "su", "root", "iptables", "-t", "nat", "-L", NULL);
 
     run_command("WIFI NETWORKS", 20,
             "su", "root", "wpa_cli", "list_networks", NULL);
@@ -121,6 +126,21 @@ static void dumpstate() {
             "su", "root", "wlutil", "counters", NULL);
 #endif
 
+    char ril_dumpstate_timeout[PROPERTY_VALUE_MAX] = {0};
+    property_get("ril.dumpstate.timeout", ril_dumpstate_timeout, "30");
+    if (strnlen(ril_dumpstate_timeout, PROPERTY_VALUE_MAX - 1) > 0) {
+        if (0 == strncmp(build_type, "user", PROPERTY_VALUE_MAX - 1)) {
+            // su does not exist on user builds, so try running without it.
+            // This way any implementations of vril-dump that do not require
+            // root can run on user builds.
+            run_command("DUMP VENDOR RIL LOGS", atoi(ril_dumpstate_timeout),
+                    "vril-dump", NULL);
+        } else {
+            run_command("DUMP VENDOR RIL LOGS", atoi(ril_dumpstate_timeout),
+                    "su", "root", "vril-dump", NULL);
+        }
+    }
+
     print_properties();
 
     run_command("KERNEL LOG", 20, "dmesg", NULL);
@@ -254,7 +274,7 @@ int main(int argc, char *argv[]) {
 
     if (getuid() == 0) {
         /* switch to non-root user and group */
-        gid_t groups[] = { AID_LOG, AID_SDCARD_RW, AID_MOUNT };
+        gid_t groups[] = { AID_LOG, AID_SDCARD_RW, AID_MOUNT, AID_INET };
         if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
             LOGE("Unable to setgroups, aborting: %s\n", strerror(errno));
             return -1;
index ccd65de..c9351af 100644 (file)
@@ -416,7 +416,7 @@ public class Notification implements Parcelable
         if (this.largeIcon != null) {
             that.largeIcon = Bitmap.createBitmap(this.largeIcon);
         }
-        that.iconLevel = that.iconLevel;
+        that.iconLevel = this.iconLevel;
         that.sound = this.sound; // android.net.Uri is immutable
         that.audioStreamType = this.audioStreamType;
 
index 9855709..6f3a2e7 100644 (file)
@@ -26,8 +26,8 @@ import android.server.BluetoothA2dpService;
 import android.server.BluetoothService;
 import android.util.Log;
 
-import com.android.internal.util.HierarchicalState;
-import com.android.internal.util.HierarchicalStateMachine;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
 
 import java.util.Set;
 
@@ -57,7 +57,7 @@ import java.util.Set;
  * Todo(): Write tests for this class, when the Android Mock support is completed.
  * @hide
  */
-public final class BluetoothDeviceProfileState extends HierarchicalStateMachine {
+public final class BluetoothDeviceProfileState extends StateMachine {
     private static final String TAG = "BluetoothDeviceProfileState";
     private static final boolean DBG = false;
 
@@ -235,16 +235,16 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
         }
     }
 
-    private class BondedDevice extends HierarchicalState {
+    private class BondedDevice extends State {
         @Override
-        protected void enter() {
+        public void enter() {
             Log.i(TAG, "Entering ACL Connected state with: " + getCurrentMessage().what);
             Message m = new Message();
             m.copyFrom(getCurrentMessage());
             sendMessageAtFrontOfQueue(m);
         }
         @Override
-        protected boolean processMessage(Message message) {
+        public boolean processMessage(Message message) {
             log("ACL Connected State -> Processing Message: " + message.what);
             switch(message.what) {
                 case CONNECT_HFP_OUTGOING:
@@ -353,12 +353,12 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
         }
     }
 
-    private class OutgoingHandsfree extends HierarchicalState {
+    private class OutgoingHandsfree extends State {
         private boolean mStatus = false;
         private int mCommand;
 
         @Override
-        protected void enter() {
+        public void enter() {
             Log.i(TAG, "Entering OutgoingHandsfree state with: " + getCurrentMessage().what);
             mCommand = getCurrentMessage().what;
             if (mCommand != CONNECT_HFP_OUTGOING &&
@@ -374,7 +374,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
         }
 
         @Override
-        protected boolean processMessage(Message message) {
+        public boolean processMessage(Message message) {
             log("OutgoingHandsfree State -> Processing Message: " + message.what);
             Message deferMsg = new Message();
             int command = message.what;
@@ -466,12 +466,12 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
         }
     }
 
-    private class IncomingHandsfree extends HierarchicalState {
+    private class IncomingHandsfree extends State {
         private boolean mStatus = false;
         private int mCommand;
 
         @Override
-        protected void enter() {
+        public void enter() {
             Log.i(TAG, "Entering IncomingHandsfree state with: " + getCurrentMessage().what);
             mCommand = getCurrentMessage().what;
             if (mCommand != CONNECT_HFP_INCOMING &&
@@ -487,7 +487,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
         }
 
         @Override
-        protected boolean processMessage(Message message) {
+        public boolean processMessage(Message message) {
             log("IncomingHandsfree State -> Processing Message: " + message.what);
             switch(message.what) {
                 case CONNECT_HFP_OUTGOING:
@@ -546,12 +546,12 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
         }
     }
 
-    private class OutgoingA2dp extends HierarchicalState {
+    private class OutgoingA2dp extends State {
         private boolean mStatus = false;
         private int mCommand;
 
         @Override
-        protected void enter() {
+        public void enter() {
             Log.i(TAG, "Entering OutgoingA2dp state with: " + getCurrentMessage().what);
             mCommand = getCurrentMessage().what;
             if (mCommand != CONNECT_A2DP_OUTGOING &&
@@ -567,7 +567,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
         }
 
         @Override
-        protected boolean processMessage(Message message) {
+        public boolean processMessage(Message message) {
             log("OutgoingA2dp State->Processing Message: " + message.what);
             Message deferMsg = new Message();
             switch(message.what) {
@@ -656,12 +656,12 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
         }
     }
 
-    private class IncomingA2dp extends HierarchicalState {
+    private class IncomingA2dp extends State {
         private boolean mStatus = false;
         private int mCommand;
 
         @Override
-        protected void enter() {
+        public void enter() {
             Log.i(TAG, "Entering IncomingA2dp state with: " + getCurrentMessage().what);
             mCommand = getCurrentMessage().what;
             if (mCommand != CONNECT_A2DP_INCOMING &&
@@ -677,7 +677,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
         }
 
         @Override
-        protected boolean processMessage(Message message) {
+        public boolean processMessage(Message message) {
             log("IncomingA2dp State->Processing Message: " + message.what);
             Message deferMsg = new Message();
             switch(message.what) {
@@ -735,12 +735,12 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
     }
 
 
-    private class OutgoingHid extends HierarchicalState {
+    private class OutgoingHid extends State {
         private boolean mStatus = false;
         private int mCommand;
 
         @Override
-        protected void enter() {
+        public void enter() {
             log("Entering OutgoingHid state with: " + getCurrentMessage().what);
             mCommand = getCurrentMessage().what;
             if (mCommand != CONNECT_HID_OUTGOING &&
@@ -752,7 +752,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
         }
 
         @Override
-        protected boolean processMessage(Message message) {
+        public boolean processMessage(Message message) {
             log("OutgoingHid State->Processing Message: " + message.what);
             Message deferMsg = new Message();
             switch(message.what) {
@@ -816,12 +816,12 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
         }
     }
 
-  private class IncomingHid extends HierarchicalState {
+  private class IncomingHid extends State {
       private boolean mStatus = false;
       private int mCommand;
 
       @Override
-      protected void enter() {
+    public void enter() {
           log("Entering IncomingHid state with: " + getCurrentMessage().what);
           mCommand = getCurrentMessage().what;
           if (mCommand != CONNECT_HID_INCOMING &&
@@ -833,7 +833,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
       }
 
       @Override
-      protected boolean processMessage(Message message) {
+    public boolean processMessage(Message message) {
           log("IncomingHid State->Processing Message: " + message.what);
           Message deferMsg = new Message();
           switch(message.what) {
index 18060a0..98afdb8 100644 (file)
@@ -22,8 +22,8 @@ import android.content.IntentFilter;
 import android.os.Message;
 import android.util.Log;
 
-import com.android.internal.util.HierarchicalState;
-import com.android.internal.util.HierarchicalStateMachine;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
 
 /**
  * This state machine is used to serialize the connections
@@ -39,7 +39,7 @@ import com.android.internal.util.HierarchicalStateMachine;
  * @hide
  */
 
-public class BluetoothProfileState extends HierarchicalStateMachine {
+public class BluetoothProfileState extends StateMachine {
     private static final boolean DBG = true;
     private static final String TAG = "BluetoothProfileState";
 
@@ -101,15 +101,15 @@ public class BluetoothProfileState extends HierarchicalStateMachine {
         context.registerReceiver(mBroadcastReceiver, filter);
     }
 
-    private class StableState extends HierarchicalState {
+    private class StableState extends State {
         @Override
-        protected void enter() {
+        public void enter() {
             log("Entering Stable State");
             mPendingDevice = null;
         }
 
         @Override
-        protected boolean processMessage(Message msg) {
+        public boolean processMessage(Message msg) {
             if (msg.what != TRANSITION_TO_STABLE) {
                 transitionTo(mPendingCommandState);
             }
@@ -117,15 +117,15 @@ public class BluetoothProfileState extends HierarchicalStateMachine {
         }
     }
 
-    private class PendingCommandState extends HierarchicalState {
+    private class PendingCommandState extends State {
         @Override
-        protected void enter() {
+        public void enter() {
             log("Entering PendingCommandState State");
             dispatchMessage(getCurrentMessage());
         }
 
         @Override
-        protected boolean processMessage(Message msg) {
+        public boolean processMessage(Message msg) {
             if (msg.what == TRANSITION_TO_STABLE) {
                 transitionTo(mStableState);
             } else {
index c08f14f..a7b0037 100644 (file)
@@ -300,4 +300,8 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker {
         msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
         msg.sendToTarget();
     }
+
+    public void setDependencyMet(boolean met) {
+        // not supported on this network
+    }
 }
index b305e02..5b8076e 100644 (file)
@@ -164,6 +164,12 @@ public class ConnectivityManager
     public static final String EXTRA_ERRORED_TETHER = "erroredArray";
 
     /**
+     * The absence of APN..
+     * @hide
+     */
+    public static final int TYPE_NONE        = -1;
+
+    /**
      * The Default Mobile data connection.  When active, all data traffic
      * will use this connection by default.
      */
@@ -218,15 +224,35 @@ public class ConnectivityManager
 
     /** {@hide} */
     public static final int TYPE_DUMMY       = 8;
+
     /**
      * The Default Ethernet data connection.  When active, all data traffic
      * will use this connection by default.
      */
     public static final int TYPE_ETHERNET    = 9;
-    /** {@hide} TODO: Need to adjust this for WiMAX. */
-    public static final int MAX_RADIO_TYPE   = TYPE_ETHERNET;
-    /** {@hide} TODO: Need to adjust this for WiMAX. */
-    public static final int MAX_NETWORK_TYPE = TYPE_ETHERNET;
+    /**
+     * Over the air Adminstration.
+     * {@hide}
+     */
+    public static final int TYPE_MOBILE_FOTA = 10;
+
+    /**
+     * IP Multimedia Subsystem
+     * {@hide}
+     */
+    public static final int TYPE_MOBILE_IMS  = 11;
+
+    /**
+     * Carrier Branded Services
+     * {@hide}
+     */
+    public static final int TYPE_MOBILE_CBS  = 12;
+
+    /** {@hide} */
+    public static final int MAX_RADIO_TYPE   = TYPE_MOBILE_CBS;
+
+    /** {@hide} */
+    public static final int MAX_NETWORK_TYPE = TYPE_MOBILE_CBS;
 
     public static final int DEFAULT_NETWORK_PREFERENCE = TYPE_WIFI;
 
@@ -670,4 +696,16 @@ public class ConnectivityManager
             return null;
         }
     }
+
+    /**
+     * @param networkType The network who's dependence has changed
+     * @param met Boolean - true if network use is ok, false if not
+     * {@hide}
+     */
+    public void setDataDependency(int networkType, boolean met) {
+        try {
+            mService.setDataDependency(networkType, met);
+        } catch (RemoteException e) {
+        }
+    }
 }
index 7396669..860da0a 100644 (file)
@@ -22,6 +22,8 @@ import android.util.Log;
 import java.net.InetAddress;
 import java.net.Inet4Address;
 import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collection;
 
 /**
  * A simple object for retrieving the results of a DHCP request.
@@ -31,7 +33,6 @@ import java.net.UnknownHostException;
 public class DhcpInfoInternal {
     private final static String TAG = "DhcpInfoInternal";
     public String ipAddress;
-    public String gateway;
     public int prefixLength;
 
     public String dns1;
@@ -40,7 +41,14 @@ public class DhcpInfoInternal {
     public String serverAddress;
     public int leaseDuration;
 
+    private Collection<RouteInfo> routes;
+
     public DhcpInfoInternal() {
+        routes = new ArrayList<RouteInfo>();
+    }
+
+    public void addRoute(RouteInfo routeInfo) {
+        routes.add(routeInfo);
     }
 
     private int convertToInt(String addr) {
@@ -58,7 +66,12 @@ public class DhcpInfoInternal {
     public DhcpInfo makeDhcpInfo() {
         DhcpInfo info = new DhcpInfo();
         info.ipAddress = convertToInt(ipAddress);
-        info.gateway = convertToInt(gateway);
+        for (RouteInfo route : routes) {
+            if (route.isDefaultRoute()) {
+                info.gateway = convertToInt(route.getGateway().getHostAddress());
+                break;
+            }
+        }
         try {
             InetAddress inetAddress = NetworkUtils.numericToInetAddress(ipAddress);
             info.netmask = NetworkUtils.prefixLengthToNetmaskInt(prefixLength);
@@ -81,8 +94,8 @@ public class DhcpInfoInternal {
     public LinkProperties makeLinkProperties() {
         LinkProperties p = new LinkProperties();
         p.addLinkAddress(makeLinkAddress());
-        if (TextUtils.isEmpty(gateway) == false) {
-            p.addGateway(NetworkUtils.numericToInetAddress(gateway));
+        for (RouteInfo route : routes) {
+            p.addRoute(route);
         }
         if (TextUtils.isEmpty(dns1) == false) {
             p.addDns(NetworkUtils.numericToInetAddress(dns1));
@@ -98,8 +111,10 @@ public class DhcpInfoInternal {
     }
 
     public String toString() {
+        String routeString = "";
+        for (RouteInfo route : routes) routeString += route.toString() + " | ";
         return "addr: " + ipAddress + "/" + prefixLength +
-                " gateway: " + gateway +
+                " routes: " + routeString +
                 " dns: " + dns1 + "," + dns2 +
                 " dhcpServer: " + serverAddress +
                 " leaseDuration: " + leaseDuration;
index ca95832..445b2f7 100644 (file)
@@ -17,8 +17,8 @@
 package android.net;
 
 import com.android.internal.util.Protocol;
-import com.android.internal.util.HierarchicalState;
-import com.android.internal.util.HierarchicalStateMachine;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
 
 import android.app.AlarmManager;
 import android.app.PendingIntent;
@@ -47,14 +47,14 @@ import android.util.Log;
  *
  * @hide
  */
-public class DhcpStateMachine extends HierarchicalStateMachine {
+public class DhcpStateMachine extends StateMachine {
 
     private static final String TAG = "DhcpStateMachine";
     private static final boolean DBG = false;
 
 
     /* A StateMachine that controls the DhcpStateMachine */
-    private HierarchicalStateMachine mController;
+    private StateMachine mController;
 
     private Context mContext;
     private BroadcastReceiver mBroadcastReceiver;
@@ -98,13 +98,13 @@ public class DhcpStateMachine extends HierarchicalStateMachine {
     public static final int DHCP_SUCCESS = 1;
     public static final int DHCP_FAILURE = 2;
 
-    private HierarchicalState mDefaultState = new DefaultState();
-    private HierarchicalState mStoppedState = new StoppedState();
-    private HierarchicalState mWaitBeforeStartState = new WaitBeforeStartState();
-    private HierarchicalState mRunningState = new RunningState();
-    private HierarchicalState mWaitBeforeRenewalState = new WaitBeforeRenewalState();
+    private State mDefaultState = new DefaultState();
+    private State mStoppedState = new StoppedState();
+    private State mWaitBeforeStartState = new WaitBeforeStartState();
+    private State mRunningState = new RunningState();
+    private State mWaitBeforeRenewalState = new WaitBeforeRenewalState();
 
-    private DhcpStateMachine(Context context, HierarchicalStateMachine controller, String intf) {
+    private DhcpStateMachine(Context context, StateMachine controller, String intf) {
         super(TAG);
 
         mContext = context;
@@ -140,7 +140,7 @@ public class DhcpStateMachine extends HierarchicalStateMachine {
         setInitialState(mStoppedState);
     }
 
-    public static DhcpStateMachine makeDhcpStateMachine(Context context, HierarchicalStateMachine controller,
+    public static DhcpStateMachine makeDhcpStateMachine(Context context, StateMachine controller,
             String intf) {
         DhcpStateMachine dsm = new DhcpStateMachine(context, controller, intf);
         dsm.start();
@@ -160,7 +160,7 @@ public class DhcpStateMachine extends HierarchicalStateMachine {
         mRegisteredForPreDhcpNotification = true;
     }
 
-    class DefaultState extends HierarchicalState {
+    class DefaultState extends State {
         @Override
         public boolean processMessage(Message message) {
             if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
@@ -169,7 +169,7 @@ public class DhcpStateMachine extends HierarchicalStateMachine {
                     Log.e(TAG, "Error! Failed to handle a DHCP renewal on " + mInterfaceName);
                     mDhcpRenewWakeLock.release();
                     break;
-                case HSM_QUIT_CMD:
+                case SM_QUIT_CMD:
                     mContext.unregisterReceiver(mBroadcastReceiver);
                     //let parent kill the state machine
                     return NOT_HANDLED;
@@ -182,7 +182,7 @@ public class DhcpStateMachine extends HierarchicalStateMachine {
     }
 
 
-    class StoppedState extends HierarchicalState {
+    class StoppedState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -215,7 +215,7 @@ public class DhcpStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class WaitBeforeStartState extends HierarchicalState {
+    class WaitBeforeStartState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -247,7 +247,7 @@ public class DhcpStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class RunningState extends HierarchicalState {
+    class RunningState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -288,7 +288,7 @@ public class DhcpStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class WaitBeforeRenewalState extends HierarchicalState {
+    class WaitBeforeRenewalState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
index d0c77cf..e39725a 100644 (file)
@@ -191,6 +191,10 @@ public class DummyDataStateTracker implements NetworkStateTracker {
         return new LinkCapabilities(mLinkCapabilities);
     }
 
+    public void setDependencyMet(boolean met) {
+        // not supported on this network
+    }
+
     static private void log(String s) {
         Slog.d(TAG, s);
     }
index df5fdd0..55850c9 100644 (file)
@@ -330,4 +330,8 @@ public class EthernetDataTracker implements NetworkStateTracker {
     public String getTcpBufferSizesPropName() {
         return "net.tcp.buffersize.wifi";
     }
+
+    public void setDependencyMet(boolean met) {
+        // not supported on this network
+    }
 }
index 70ab4f1..8be492c 100644 (file)
@@ -92,4 +92,6 @@ interface IConnectivityManager
     void setGlobalProxy(in ProxyProperties p);
 
     ProxyProperties getProxy();
+
+    void setDataDependency(int networkType, boolean met);
 }
index 9c36b12..f6a114c 100644 (file)
@@ -78,6 +78,14 @@ public class LinkAddress implements Parcelable {
             this.prefixLength == linkAddress.prefixLength;
     }
 
+    @Override
+    /*
+     * generate hashcode based on significant fields
+     */
+    public int hashCode() {
+        return ((null == address) ? 0 : address.hashCode()) + prefixLength;
+    }
+
     /**
      * Returns the InetAddress for this address.
      */
index b6e9751..19894a0 100644 (file)
@@ -19,11 +19,9 @@ package android.net;
 import android.net.ProxyProperties;
 import android.os.Parcelable;
 import android.os.Parcel;
-import android.util.Log;
+import android.text.TextUtils;
 
 import java.net.InetAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -56,7 +54,7 @@ public class LinkProperties implements Parcelable {
     String mIfaceName;
     private Collection<LinkAddress> mLinkAddresses;
     private Collection<InetAddress> mDnses;
-    private Collection<InetAddress> mGateways;
+    private Collection<RouteInfo> mRoutes;
     private ProxyProperties mHttpProxy;
 
     public LinkProperties() {
@@ -69,8 +67,9 @@ public class LinkProperties implements Parcelable {
             mIfaceName = source.getInterfaceName();
             mLinkAddresses = source.getLinkAddresses();
             mDnses = source.getDnses();
-            mGateways = source.getGateways();
-            mHttpProxy = new ProxyProperties(source.getHttpProxy());
+            mRoutes = source.getRoutes();
+            mHttpProxy = (source.getHttpProxy() == null)  ?
+                null : new ProxyProperties(source.getHttpProxy());
         }
     }
 
@@ -91,7 +90,7 @@ public class LinkProperties implements Parcelable {
     }
 
     public void addLinkAddress(LinkAddress address) {
-        mLinkAddresses.add(address);
+        if (address != null) mLinkAddresses.add(address);
     }
 
     public Collection<LinkAddress> getLinkAddresses() {
@@ -99,18 +98,18 @@ public class LinkProperties implements Parcelable {
     }
 
     public void addDns(InetAddress dns) {
-        mDnses.add(dns);
+        if (dns != null) mDnses.add(dns);
     }
 
     public Collection<InetAddress> getDnses() {
         return Collections.unmodifiableCollection(mDnses);
     }
 
-    public void addGateway(InetAddress gateway) {
-        mGateways.add(gateway);
+    public void addRoute(RouteInfo route) {
+        if (route != null) mRoutes.add(route);
     }
-    public Collection<InetAddress> getGateways() {
-        return Collections.unmodifiableCollection(mGateways);
+    public Collection<RouteInfo> getRoutes() {
+        return Collections.unmodifiableCollection(mRoutes);
     }
 
     public void setHttpProxy(ProxyProperties proxy) {
@@ -124,7 +123,7 @@ public class LinkProperties implements Parcelable {
         mIfaceName = null;
         mLinkAddresses = new ArrayList<LinkAddress>();
         mDnses = new ArrayList<InetAddress>();
-        mGateways = new ArrayList<InetAddress>();
+        mRoutes = new ArrayList<RouteInfo>();
         mHttpProxy = null;
     }
 
@@ -141,19 +140,80 @@ public class LinkProperties implements Parcelable {
         String ifaceName = (mIfaceName == null ? "" : "InterfaceName: " + mIfaceName + " ");
 
         String linkAddresses = "LinkAddresses: [";
-        for (LinkAddress addr : mLinkAddresses) linkAddresses += addr.toString();
+        for (LinkAddress addr : mLinkAddresses) linkAddresses += addr.toString() + ",";
         linkAddresses += "] ";
 
         String dns = "DnsAddresses: [";
         for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ",";
         dns += "] ";
 
-        String gateways = "Gateways: [";
-        for (InetAddress gw : mGateways) gateways += gw.getHostAddress() + ",";
-        gateways += "] ";
+        String routes = "Routes: [";
+        for (RouteInfo route : mRoutes) routes += route.toString() + ",";
+        routes += "] ";
         String proxy = (mHttpProxy == null ? "" : "HttpProxy: " + mHttpProxy.toString() + " ");
 
-        return ifaceName + linkAddresses + gateways + dns + proxy;
+        return ifaceName + linkAddresses + routes + dns + proxy;
+    }
+
+
+    @Override
+    /**
+     * Compares this {@code LinkProperties} instance against the target
+     * LinkProperties in {@code obj}. Two LinkPropertieses are equal if
+     * all their fields are equal in values.
+     *
+     * For collection fields, such as mDnses, containsAll() is used to check
+     * if two collections contains the same elements, independent of order.
+     * There are two thoughts regarding containsAll()
+     * 1. Duplicated elements. eg, (A, B, B) and (A, A, B) are equal.
+     * 2. Worst case performance is O(n^2).
+     *
+     * @param obj the object to be tested for equality.
+     * @return {@code true} if both objects are equal, {@code false} otherwise.
+     */
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+
+        if (!(obj instanceof LinkProperties)) return false;
+
+        boolean sameAddresses;
+        boolean sameDnses;
+        boolean sameRoutes;
+
+        LinkProperties target = (LinkProperties) obj;
+
+        Collection<InetAddress> targetAddresses = target.getAddresses();
+        Collection<InetAddress> sourceAddresses = getAddresses();
+        sameAddresses = (sourceAddresses.size() == targetAddresses.size()) ?
+                sourceAddresses.containsAll(targetAddresses) : false;
+
+        Collection<InetAddress> targetDnses = target.getDnses();
+        sameDnses = (mDnses.size() == targetDnses.size()) ?
+                mDnses.containsAll(targetDnses) : false;
+
+        Collection<RouteInfo> targetRoutes = target.getRoutes();
+        sameRoutes = (mRoutes.size() == targetRoutes.size()) ?
+                mRoutes.containsAll(targetRoutes) : false;
+
+        return
+            sameAddresses && sameDnses && sameRoutes
+            && TextUtils.equals(getInterfaceName(), target.getInterfaceName())
+            && (getHttpProxy() == null ? target.getHttpProxy() == null :
+                getHttpProxy().equals(target.getHttpProxy()));
+    }
+
+    @Override
+    /**
+     * generate hashcode based on significant fields
+     * Equal objects must produce the same hash code, while unequal objects
+     * may have the same hash codes.
+     */
+    public int hashCode() {
+        return ((null == mIfaceName) ? 0 : mIfaceName.hashCode()
+                + mLinkAddresses.size() * 31
+                + mDnses.size() * 37
+                + mRoutes.size() * 41
+                + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode()));
     }
 
     /**
@@ -172,9 +232,9 @@ public class LinkProperties implements Parcelable {
             dest.writeByteArray(d.getAddress());
         }
 
-        dest.writeInt(mGateways.size());
-        for(InetAddress gw : mGateways) {
-            dest.writeByteArray(gw.getAddress());
+        dest.writeInt(mRoutes.size());
+        for(RouteInfo route : mRoutes) {
+            dest.writeParcelable(route, flags);
         }
 
         if (mHttpProxy != null) {
@@ -213,9 +273,7 @@ public class LinkProperties implements Parcelable {
                 }
                 addressCount = in.readInt();
                 for (int i=0; i<addressCount; i++) {
-                    try {
-                        netProp.addGateway(InetAddress.getByAddress(in.createByteArray()));
-                    } catch (UnknownHostException e) { }
+                    netProp.addRoute((RouteInfo)in.readParcelable(null));
                 }
                 if (in.readByte() == 1) {
                     netProp.setHttpProxy((ProxyProperties)in.readParcelable(null));
index e04964e..f3c863f 100644 (file)
@@ -20,6 +20,7 @@ import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.os.Bundle;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Messenger;
@@ -68,10 +69,6 @@ public class MobileDataStateTracker implements NetworkStateTracker {
     private boolean mPrivateDnsRouteSet = false;
     private boolean mDefaultRouteSet = false;
 
-    // DEFAULT and HIPRI are the same connection.  If we're one of these we need to check if
-    // the other is also disconnected before we reset sockets
-    private boolean mIsDefaultOrHipri = false;
-
     private Handler mHandler;
     private AsyncChannel mDataConnectionTrackerAc;
     private Messenger mMessenger;
@@ -86,12 +83,6 @@ public class MobileDataStateTracker implements NetworkStateTracker {
                 TelephonyManager.getDefault().getNetworkType(), tag,
                 TelephonyManager.getDefault().getNetworkTypeName());
         mApnType = networkTypeToApnType(netType);
-        if (netType == ConnectivityManager.TYPE_MOBILE ||
-                netType == ConnectivityManager.TYPE_MOBILE_HIPRI) {
-            mIsDefaultOrHipri = true;
-        }
-
-        mPhoneService = null;
     }
 
     /**
@@ -179,8 +170,6 @@ public class MobileDataStateTracker implements NetworkStateTracker {
     }
 
     private class MobileDataStateReceiver extends BroadcastReceiver {
-        IConnectivityManager mConnectivityManager;
-
         @Override
         public void onReceive(Context context, Intent intent) {
             if (intent.getAction().equals(TelephonyIntents.
@@ -217,35 +206,6 @@ public class MobileDataStateTracker implements NetworkStateTracker {
                             }
 
                             setDetailedState(DetailedState.DISCONNECTED, reason, apnName);
-                            boolean doReset = true;
-                            if (mIsDefaultOrHipri == true) {
-                                // both default and hipri must go down before we reset
-                                int typeToCheck = (Phone.APN_TYPE_DEFAULT.equals(mApnType) ?
-                                    ConnectivityManager.TYPE_MOBILE_HIPRI :
-                                    ConnectivityManager.TYPE_MOBILE);
-                                if (mConnectivityManager == null) {
-                                    IBinder b = ServiceManager.getService(
-                                            Context.CONNECTIVITY_SERVICE);
-                                    mConnectivityManager = IConnectivityManager.Stub.asInterface(b);
-                                }
-                                try {
-                                    if (mConnectivityManager != null) {
-                                        NetworkInfo info = mConnectivityManager.getNetworkInfo(
-                                                typeToCheck);
-                                        if (info.isConnected() == true) {
-                                            doReset = false;
-                                        }
-                                    }
-                                } catch (RemoteException e) {
-                                    // just go ahead with the reset
-                                    loge("Exception trying to contact ConnService: " + e);
-                                }
-                            }
-                            if (doReset && mLinkProperties != null) {
-                                String iface = mLinkProperties.getInterfaceName();
-                                if (iface != null) NetworkUtils.resetConnections(iface);
-                            }
-                            // TODO - check this
                             // can't do this here - ConnectivityService needs it to clear stuff
                             // it's ok though - just leave it to be refreshed next time
                             // we connect.
@@ -275,6 +235,21 @@ public class MobileDataStateTracker implements NetworkStateTracker {
                             setDetailedState(DetailedState.CONNECTED, reason, apnName);
                             break;
                     }
+                } else {
+                    // There was no state change. Check if LinkProperties has been updated.
+                    if (TextUtils.equals(reason, Phone.REASON_LINK_PROPERTIES_CHANGED)) {
+                        mLinkProperties = intent.getParcelableExtra(Phone.DATA_LINK_PROPERTIES_KEY);
+                        if (mLinkProperties == null) {
+                            log("No link property in LINK_PROPERTIES change event.");
+                            mLinkProperties = new LinkProperties();
+                        }
+                        // Just update reason field in this NetworkInfo
+                        mNetworkInfo.setDetailedState(mNetworkInfo.getDetailedState(), reason,
+                                                      mNetworkInfo.getExtraInfo());
+                        Message msg = mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED,
+                                                            mNetworkInfo);
+                        msg.sendToTarget();
+                    }
                 }
             } else if (intent.getAction().
                     equals(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED)) {
@@ -437,7 +412,8 @@ public class MobileDataStateTracker implements NetworkStateTracker {
                 retValue = true;
                 break;
             case Phone.APN_REQUEST_STARTED:
-                // no need to do anything - we're already due some status update intents
+                // set IDLE here , avoid the following second FAILED not sent out
+                mNetworkInfo.setDetailedState(DetailedState.IDLE, null, null);
                 retValue = true;
                 break;
             case Phone.APN_REQUEST_FAILED:
@@ -492,6 +468,25 @@ public class MobileDataStateTracker implements NetworkStateTracker {
         }
     }
 
+    /**
+     * carrier dependency is met/unmet
+     * @param met
+     */
+    public void setDependencyMet(boolean met) {
+        Bundle bundle = Bundle.forPair(DataConnectionTracker.APN_TYPE_KEY, mApnType);
+        try {
+            log("setDependencyMet: E met=" + met);
+            Message msg = Message.obtain();
+            msg.what = DataConnectionTracker.CMD_SET_DEPENDENCY_MET;
+            msg.arg1 = (met ? DataConnectionTracker.ENABLED : DataConnectionTracker.DISABLED);
+            msg.setData(bundle);
+            mDataConnectionTrackerAc.sendMessage(msg);
+            log("setDependencyMet: X met=" + met);
+        } catch (NullPointerException e) {
+            log("setDependencyMet: X mAc was null" + e);
+        }
+    }
+
     @Override
     public String toString() {
         StringBuffer sb = new StringBuffer("Mobile data state: ");
@@ -546,6 +541,12 @@ public class MobileDataStateTracker implements NetworkStateTracker {
                 return Phone.APN_TYPE_DUN;
             case ConnectivityManager.TYPE_MOBILE_HIPRI:
                 return Phone.APN_TYPE_HIPRI;
+            case ConnectivityManager.TYPE_MOBILE_FOTA:
+                return Phone.APN_TYPE_FOTA;
+            case ConnectivityManager.TYPE_MOBILE_IMS:
+                return Phone.APN_TYPE_IMS;
+            case ConnectivityManager.TYPE_MOBILE_CBS:
+                return Phone.APN_TYPE_CBS;
             default:
                 sloge("Error mapping networkType " + netType + " to apnType.");
                 return null;
diff --git a/core/java/android/net/NetworkConfig.java b/core/java/android/net/NetworkConfig.java
new file mode 100644 (file)
index 0000000..3cc0bc5
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2010 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.net;
+
+import android.util.Log;
+
+/**
+ * Describes the buildtime configuration of a network.
+ * Holds settings read from resources.
+ * @hide
+ */
+public class NetworkConfig {
+    /**
+     * Human readable string
+     */
+    public String name;
+
+    /**
+     * Type from ConnectivityManager
+     */
+    public int type;
+
+    /**
+     * the radio number from radio attributes config
+     */
+    public int radio;
+
+    /**
+     * higher number == higher priority when turning off connections
+     */
+    public int priority;
+
+    /**
+     * indicates the boot time dependencyMet setting
+     */
+    public boolean dependencyMet;
+
+    /**
+     * indicates the default restoral timer in seconds
+     * if the network is used as a special network feature
+     * -1 indicates no restoration of default
+     */
+    public int restoreTime;
+
+    /**
+     * input string from config.xml resource.  Uses the form:
+     * [Connection name],[ConnectivityManager connection type],
+     * [associated radio-type],[priority],[dependencyMet]
+     */
+    public NetworkConfig(String init) {
+        String fragments[] = init.split(",");
+        name = fragments[0].trim().toLowerCase();
+        type = Integer.parseInt(fragments[1]);
+        radio = Integer.parseInt(fragments[2]);
+        priority = Integer.parseInt(fragments[3]);
+        restoreTime = Integer.parseInt(fragments[4]);
+        dependencyMet = Boolean.parseBoolean(fragments[5]);
+    }
+
+    /**
+     * Indicates if this network is supposed to be default-routable
+     */
+    public boolean isDefault() {
+        return (type == radio);
+    }
+}
index eb97d77..f53063d 100644 (file)
@@ -176,4 +176,9 @@ public interface NetworkStateTracker {
      * Indicate tear down requested from connectivity
      */
     public void setTeardownRequested(boolean isRequested);
+
+    /**
+     * An external dependency has been met/unmet
+     */
+    public void setDependencyMet(boolean met);
 }
index 823d10f..76534ef 100644 (file)
@@ -38,34 +38,22 @@ public class NetworkUtils {
     /** Bring the named network interface down. */
     public native static int disableInterface(String interfaceName);
 
-    /**
-     * Add a route to the routing table.
-     *
-     * @param interfaceName the interface to route through.
-     * @param dst the network or host to route to. May be IPv4 or IPv6, e.g.
-     * "0.0.0.0" or "2001:4860::".
-     * @param prefixLength the prefix length of the route.
-     * @param gw the gateway to use, e.g., "192.168.251.1". If null,
-     * indicates a directly-connected route.
-     */
-    public native static int addRoute(String interfaceName, String dst,
-          int prefixLength, String gw);
-
-    /** Return the gateway address for the default route for the named interface. */
-    public static InetAddress getDefaultRoute(String interfaceName) {
-        int addr = getDefaultRouteNative(interfaceName);
-        return intToInetAddress(addr);
-    }
-    private native static int getDefaultRouteNative(String interfaceName);
+    /** Setting bit 0 indicates reseting of IPv4 addresses required */
+    public static final int RESET_IPV4_ADDRESSES = 0x01;
 
-    /** Remove host routes that uses the named interface. */
-    public native static int removeHostRoutes(String interfaceName);
+    /** Setting bit 1 indicates reseting of IPv4 addresses required */
+    public static final int RESET_IPV6_ADDRESSES = 0x02;
 
-    /** Remove the default route for the named interface. */
-    public native static int removeDefaultRoute(String interfaceName);
+    /** Reset all addresses */
+    public static final int RESET_ALL_ADDRESSES = RESET_IPV4_ADDRESSES | RESET_IPV6_ADDRESSES;
 
-    /** Reset any sockets that are connected via the named interface. */
-    public native static int resetConnections(String interfaceName);
+    /**
+     * Reset IPv6 or IPv4 sockets that are connected via the named interface.
+     *
+     * @param interfaceName is the interface to reset
+     * @param mask {@see #RESET_IPV4_ADDRESSES} and {@see #RESET_IPV6_ADDRESSES}
+     */
+    public native static int resetConnections(String interfaceName, int mask);
 
     /**
      * Start the DHCP client daemon, in order to have it request addresses
@@ -160,6 +148,15 @@ public class NetworkUtils {
     }
 
     /**
+     * Convert a IPv4 netmask integer to a prefix length
+     * @param netmask as an integer in network byte order
+     * @return the network prefix length
+     */
+    public static int netmaskIntToPrefixLength(int netmask) {
+        return Integer.bitCount(netmask);
+    }
+
+    /**
      * Create an InetAddress from a string where the string must be a standard
      * representation of a V4 or V6 address.  Avoids doing a DNS lookup on failure
      * but it will throw an IllegalArgumentException in that case.
@@ -173,56 +170,69 @@ public class NetworkUtils {
     }
 
     /**
-     * Add a default route through the specified gateway.
-     * @param interfaceName interface on which the route should be added
-     * @param gw the IP address of the gateway to which the route is desired,
-     * @return {@code true} on success, {@code false} on failure
+     * Get InetAddress masked with prefixLength.  Will never return null.
+     * @param IP address which will be masked with specified prefixLength
+     * @param prefixLength the prefixLength used to mask the IP
      */
-    public static boolean addDefaultRoute(String interfaceName, InetAddress gw) {
-        String dstStr;
-        String gwStr = gw.getHostAddress();
-
-        if (gw instanceof Inet4Address) {
-            dstStr = "0.0.0.0";
-        } else if (gw instanceof Inet6Address) {
-            dstStr = "::";
-        } else {
-            Log.w(TAG, "addDefaultRoute failure: address is neither IPv4 nor IPv6" +
-                       "(" + gwStr + ")");
-            return false;
+    public static InetAddress getNetworkPart(InetAddress address, int prefixLength) {
+        if (address == null) {
+            throw new RuntimeException("getNetworkPart doesn't accept null address");
+        }
+
+        byte[] array = address.getAddress();
+
+        if (prefixLength < 0 || prefixLength > array.length * 8) {
+            throw new RuntimeException("getNetworkPart - bad prefixLength");
         }
-        return addRoute(interfaceName, dstStr, 0, gwStr) == 0;
+
+        int offset = prefixLength / 8;
+        int reminder = prefixLength % 8;
+        byte mask = (byte)(0xFF << (8 - reminder));
+
+        if (offset < array.length) array[offset] = (byte)(array[offset] & mask);
+
+        offset++;
+
+        for (; offset < array.length; offset++) {
+            array[offset] = 0;
+        }
+
+        InetAddress netPart = null;
+        try {
+            netPart = InetAddress.getByAddress(array);
+        } catch (UnknownHostException e) {
+            throw new RuntimeException("getNetworkPart error - " + e.toString());
+        }
+        return netPart;
     }
 
     /**
-     * Add a host route.
-     * @param interfaceName interface on which the route should be added
-     * @param dst the IP address of the host to which the route is desired,
-     * this should not be null.
-     * @param gw the IP address of the gateway to which the route is desired,
-     * if null, indicates a directly-connected route.
-     * @return {@code true} on success, {@code false} on failure
+     * Check if IP address type is consistent between two InetAddress.
+     * @return true if both are the same type.  False otherwise.
      */
-    public static boolean addHostRoute(String interfaceName, InetAddress dst,
-          InetAddress gw) {
-        if (dst == null) {
-            Log.w(TAG, "addHostRoute: dst should not be null");
-            return false;
-        }
+    public static boolean addressTypeMatches(InetAddress left, InetAddress right) {
+        return (((left instanceof Inet4Address) && (right instanceof Inet4Address)) ||
+                ((left instanceof Inet6Address) && (right instanceof Inet6Address)));
+    }
 
-        int prefixLength;
-        String dstStr = dst.getHostAddress();
-        String gwStr = (gw != null) ? gw.getHostAddress() : null;
-
-        if (dst instanceof Inet4Address) {
-            prefixLength = 32;
-        } else if (dst instanceof Inet6Address) {
-            prefixLength = 128;
-        } else {
-            Log.w(TAG, "addHostRoute failure: address is neither IPv4 nor IPv6" +
-                       "(" + dst + ")");
-            return false;
+    /**
+     * Convert a 32 char hex string into a Inet6Address.
+     * throws a runtime exception if the string isn't 32 chars, isn't hex or can't be
+     * made into an Inet6Address
+     * @param addrHexString a 32 character hex string representing an IPv6 addr
+     * @return addr an InetAddress representation for the string
+     */
+    public static InetAddress hexToInet6Address(String addrHexString)
+            throws IllegalArgumentException {
+        try {
+            return numericToInetAddress(String.format("%s:%s:%s:%s:%s:%s:%s:%s",
+                    addrHexString.substring(0,4),   addrHexString.substring(4,8),
+                    addrHexString.substring(8,12),  addrHexString.substring(12,16),
+                    addrHexString.substring(16,20), addrHexString.substring(20,24),
+                    addrHexString.substring(24,28), addrHexString.substring(28,32)));
+        } catch (Exception e) {
+            Log.e("NetworkUtils", "error in hexToInet6Address(" + addrHexString + "): " + e);
+            throw new IllegalArgumentException(e);
         }
-        return addRoute(interfaceName, dstStr, prefixLength, gwStr) == 0;
     }
 }
index cbe4445..44dbec1 100644 (file)
@@ -163,6 +163,16 @@ public class ProxyProperties implements Parcelable {
         return 0;
     }
 
+    @Override
+    /*
+     * generate hashcode based on significant fields
+     */
+    public int hashCode() {
+        return ((null == mHost) ? 0 : mHost.hashCode())
+        + ((null == mExclusionList) ? 0 : mExclusionList.hashCode())
+        + mPort;
+    }
+
     /**
      * Implement the Parcelable interface.
      * @hide
diff --git a/core/java/android/net/RouteInfo.aidl b/core/java/android/net/RouteInfo.aidl
new file mode 100644 (file)
index 0000000..2296a57
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2011 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.net;
+
+parcelable RouteInfo;
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
new file mode 100644 (file)
index 0000000..8e5ddda
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2011 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.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.UnknownHostException;
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+
+import java.util.Collection;
+
+/**
+ * A simple container for route information.
+ *
+ * @hide
+ */
+public class RouteInfo implements Parcelable {
+    /**
+     * The IP destination address for this route.
+     */
+    private final LinkAddress mDestination;
+
+    /**
+     * The gateway address for this route.
+     */
+    private final InetAddress mGateway;
+
+    private final boolean mIsDefault;
+
+    public RouteInfo(LinkAddress destination, InetAddress gateway) {
+        if (destination == null) {
+            if (gateway != null) {
+                if (gateway instanceof Inet4Address) {
+                    destination = new LinkAddress(Inet4Address.ANY, 0);
+                } else {
+                    destination = new LinkAddress(Inet6Address.ANY, 0);
+                }
+            } else {
+                // no destination, no gateway. invalid.
+                throw new RuntimeException("Invalid arguments passed in.");
+            }
+        }
+        if (gateway == null) {
+            if (destination.getAddress() instanceof Inet4Address) {
+                gateway = Inet4Address.ANY;
+            } else {
+                gateway = Inet6Address.ANY;
+            }
+        }
+        mDestination = new LinkAddress(NetworkUtils.getNetworkPart(destination.getAddress(),
+                destination.getNetworkPrefixLength()), destination.getNetworkPrefixLength());
+        mGateway = gateway;
+        mIsDefault = isDefault();
+    }
+
+    public RouteInfo(InetAddress gateway) {
+        this(null, gateway);
+    }
+
+    public static RouteInfo makeHostRoute(InetAddress host) {
+        return makeHostRoute(host, null);
+    }
+
+    public static RouteInfo makeHostRoute(InetAddress host, InetAddress gateway) {
+        if (host == null) return null;
+
+        if (host instanceof Inet4Address) {
+            return new RouteInfo(new LinkAddress(host, 32), gateway);
+        } else {
+            return new RouteInfo(new LinkAddress(host, 128), gateway);
+        }
+    }
+
+    private boolean isDefault() {
+        boolean val = false;
+        if (mGateway != null) {
+            if (mGateway instanceof Inet4Address) {
+                val = (mDestination == null || mDestination.getNetworkPrefixLength() == 0);
+            } else {
+                val = (mDestination == null || mDestination.getNetworkPrefixLength() == 0);
+            }
+        }
+        return val;
+    }
+
+    public LinkAddress getDestination() {
+        return mDestination;
+    }
+
+    public InetAddress getGateway() {
+        return mGateway;
+    }
+
+    public boolean isDefaultRoute() {
+        return mIsDefault;
+    }
+
+    public String toString() {
+        String val = "";
+        if (mDestination != null) val = mDestination.toString();
+        if (mGateway != null) val += " -> " + mGateway.getHostAddress();
+        return val;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        if (mDestination == null) {
+            dest.writeByte((byte) 0);
+        } else {
+            dest.writeByte((byte) 1);
+            dest.writeByteArray(mDestination.getAddress().getAddress());
+            dest.writeInt(mDestination.getNetworkPrefixLength());
+        }
+
+        if (mGateway == null) {
+            dest.writeByte((byte) 0);
+        } else {
+            dest.writeByte((byte) 1);
+            dest.writeByteArray(mGateway.getAddress());
+        }
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+
+        if (!(obj instanceof RouteInfo)) return false;
+
+        RouteInfo target = (RouteInfo) obj;
+
+        boolean sameDestination = ( mDestination == null) ?
+                target.getDestination() == null
+                : mDestination.equals(target.getDestination());
+
+        boolean sameAddress = (mGateway == null) ?
+                target.getGateway() == null
+                : mGateway.equals(target.getGateway());
+
+        return sameDestination && sameAddress
+            && mIsDefault == target.mIsDefault;
+    }
+
+    @Override
+    public int hashCode() {
+        return (mDestination == null ? 0 : mDestination.hashCode())
+            + (mGateway == null ? 0 :mGateway.hashCode())
+            + (mIsDefault ? 3 : 7);
+    }
+
+    public static final Creator<RouteInfo> CREATOR =
+        new Creator<RouteInfo>() {
+        public RouteInfo createFromParcel(Parcel in) {
+            InetAddress destAddr = null;
+            int prefix = 0;
+            InetAddress gateway = null;
+
+            if (in.readByte() == 1) {
+                byte[] addr = in.createByteArray();
+                prefix = in.readInt();
+
+                try {
+                    destAddr = InetAddress.getByAddress(addr);
+                } catch (UnknownHostException e) {}
+            }
+
+            if (in.readByte() == 1) {
+                byte[] addr = in.createByteArray();
+
+                try {
+                    gateway = InetAddress.getByAddress(addr);
+                } catch (UnknownHostException e) {}
+            }
+
+            LinkAddress dest = null;
+
+            if (destAddr != null) {
+                dest = new LinkAddress(destAddr, prefix);
+            }
+
+            return new RouteInfo(dest, gateway);
+        }
+
+        public RouteInfo[] newArray(int size) {
+            return new RouteInfo[size];
+        }
+    };
+
+    private boolean matches(InetAddress destination) {
+        if (destination == null) return false;
+
+        // if the destination is present and the route is default.
+        // return true
+        if (isDefault()) return true;
+
+        // match the route destination and destination with prefix length
+        InetAddress dstNet = NetworkUtils.getNetworkPart(destination,
+                mDestination.getNetworkPrefixLength());
+
+        return mDestination.getAddress().equals(dstNet);
+    }
+
+    /**
+     * Find the route from a Collection of routes that best matches a given address.
+     * May return null if no routes are applicable.
+     * @param routes a Collection of RouteInfos to chose from
+     * @param dest the InetAddress your trying to get to
+     * @return the RouteInfo from the Collection that best fits the given address
+     */
+    public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) {
+        if ((routes == null) || (dest == null)) return null;
+
+        RouteInfo bestRoute = null;
+        // pick a longest prefix match under same address type
+        for (RouteInfo route : routes) {
+            if (NetworkUtils.addressTypeMatches(route.mDestination.getAddress(), dest)) {
+                if ((bestRoute != null) &&
+                        (bestRoute.mDestination.getNetworkPrefixLength() >=
+                        route.mDestination.getNetworkPrefixLength())) {
+                    continue;
+                }
+                if (route.matches(dest)) bestRoute = route;
+            }
+        }
+        return bestRoute;
+    }
+}
index d439a48..870127c 100644 (file)
@@ -28,7 +28,7 @@ import android.nfc.ILlcpConnectionlessSocket;
 import android.nfc.INfcTag;
 import android.nfc.IP2pTarget;
 import android.nfc.IP2pInitiator;
-import android.nfc.INfcSecureElement;
+import android.nfc.INfcAdapterExtras;
 
 /**
  * @hide
@@ -41,13 +41,12 @@ interface INfcAdapter
     INfcTag getNfcTagInterface();
     IP2pTarget getP2pTargetInterface();
     IP2pInitiator getP2pInitiatorInterface();
-    INfcSecureElement getNfcSecureElementInterface();
+    INfcAdapterExtras getNfcAdapterExtrasInterface();
 
     // NfcAdapter-class related methods
     boolean isEnabled();
     NdefMessage localGet();
     void localSet(in NdefMessage message);
-    void openTagConnection(in Tag tag);
     void enableForegroundDispatch(in ComponentName activity, in PendingIntent intent,
             in IntentFilter[] filters, in TechListParcel techLists);
     void disableForegroundDispatch(in ComponentName activity);
@@ -59,12 +58,8 @@ interface INfcAdapter
     int createLlcpConnectionlessSocket(int sap);
     int createLlcpServiceSocket(int sap, String sn, int miu, int rw, int linearBufferLength);
     int createLlcpSocket(int sap, int miu, int rw, int linearBufferLength);
-    int deselectSecureElement();
     boolean disable();
     boolean enable();
     String getProperties(String param);
-    int[] getSecureElementList();
-    int getSelectedSecureElement();
-    int selectSecureElement(int seId);
     int setProperties(String param, String value);
-}
\ No newline at end of file
+}
similarity index 63%
rename from core/java/android/nfc/INfcSecureElement.aidl
rename to core/java/android/nfc/INfcAdapterExtras.aidl
index aa98dd2..ab5c1a6 100755 (executable)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2011 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.
 
 package android.nfc;
 
+import android.os.Bundle;
+
 /**
  * {@hide}
  */
-interface INfcSecureElement {
-    int openSecureElementConnection();
-    int closeSecureElementConnection(int nativeHandle);
-    byte[] exchangeAPDU(int nativeHandle, in byte[] data);
-    int[] getSecureElementTechList(int nativeHandle);
-    byte[] getSecureElementUid(int nativeHandle);
-}
\ No newline at end of file
+interface INfcAdapterExtras {
+    Bundle open(IBinder b);
+    Bundle close();
+    Bundle transceive(in byte[] data_in);
+    int getCardEmulationRoute();
+    void setCardEmulationRoute(int route);    
+}
index 8c56fda..4689804 100644 (file)
@@ -157,31 +157,6 @@ public final class NfcAdapter {
     public static final String EXTRA_ID = "android.nfc.extra.ID";
 
     /**
-     * Broadcast Action: a transaction with a secure element has been detected.
-     * <p>
-     * Always contains the extra field
-     * {@link android.nfc.NfcAdapter#EXTRA_AID}
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_TRANSACTION_DETECTED =
-            "android.nfc.action.TRANSACTION_DETECTED";
-
-    /**
-     * Broadcast Action: an RF field ON has been detected.
-     * @hide
-     */
-    public static final String ACTION_RF_FIELD_ON_DETECTED =
-            "android.nfc.action.RF_FIELD_ON_DETECTED";
-
-    /**
-     * Broadcast Action: an RF Field OFF has been detected.
-     * @hide
-     */
-    public static final String ACTION_RF_FIELD_OFF_DETECTED =
-            "android.nfc.action.RF_FIELD_OFF_DETECTED";
-
-    /**
      * Broadcast Action: an adapter's state changed between enabled and disabled.
      *
      * The new value is stored in the extra EXTRA_NEW_BOOLEAN_STATE and just contains
@@ -201,15 +176,6 @@ public final class NfcAdapter {
     public static final String EXTRA_NEW_BOOLEAN_STATE = "android.nfc.isEnabled";
 
     /**
-     * Mandatory byte array extra field in
-     * {@link android.nfc.NfcAdapter#ACTION_TRANSACTION_DETECTED}.
-     * <p>
-     * Contains the AID of the applet involved in the transaction.
-     * @hide
-     */
-    public static final String EXTRA_AID = "android.nfc.extra.AID";
-
-    /**
      * LLCP link status: The LLCP link is activated.
      * @hide
      */
@@ -691,39 +657,14 @@ public final class NfcAdapter {
     }
 
     /**
-     * Create an Nfc Secure Element Connection
      * @hide
      */
-    public NfcSecureElement createNfcSecureElementConnection() {
+    public INfcAdapterExtras getNfcAdapterExtrasInterface() {
         try {
-            return new NfcSecureElement(sService.getNfcSecureElementInterface());
+            return sService.getNfcAdapterExtrasInterface();
         } catch (RemoteException e) {
-            Log.e(TAG, "createNfcSecureElementConnection failed", e);
+            attemptDeadServiceRecovery(e);
             return null;
         }
     }
-
-    /**
-     * To change the Secure Element Card Emulation state (ON/OFF)
-     * @hide
-     */
-    public void changeNfcSecureElementCardEmulationState(boolean state)
-    {
-        int seId = 11259375;
-        if(state){
-            /* Enable card emulation */
-            try {
-                sService.selectSecureElement(seId);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Enable card emulation failed", e);
-            }
-        }else{
-            /* Disable card emulation */
-            try {
-                sService.deselectSecureElement();
-            } catch (RemoteException e) {
-                Log.e(TAG, " card emulation failed", e);
-            }
-        }
-    }
 }
diff --git a/core/java/android/nfc/NfcSecureElement.java b/core/java/android/nfc/NfcSecureElement.java
deleted file mode 100755 (executable)
index 3b5f39e..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2010 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.nfc;
-
-import android.nfc.tech.TagTechnology;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.io.IOException;
-
-//import android.util.Log;
-
-/**
- * This class provides the primary API for managing all aspects Secure Element.
- * Get an instance of this class by calling
- * Context.getSystemService(Context.NFC_SERVICE).
- * @hide
- */
-public final class NfcSecureElement {
-
-    private static final String TAG = "NfcSecureElement";
-
-    private INfcSecureElement mService;
-
-
-    /**
-     * @hide
-     */
-    public NfcSecureElement(INfcSecureElement mSecureElementService) {
-        mService = mSecureElementService;
-    }
-
-    public int openSecureElementConnection(String seType) throws IOException {
-        if (seType.equals("SmartMX")) {
-            try {
-                int handle = mService.openSecureElementConnection();
-                // Handle potential errors
-                if (handle != 0) {
-                    return handle;
-                } else {
-                    throw new IOException("SmartMX connection not allowed");
-                }
-            } catch (RemoteException e) {
-                Log.e(TAG, "RemoteException in openSecureElementConnection(): ", e);
-                return 0;
-            }
-
-        } else if (seType.equals("UICC")) {
-            return 0;
-        } else {
-               throw new IOException("Wrong Secure Element type");
-        }
-    }
-
-
-    public byte [] exchangeAPDU(int handle,byte [] data) throws IOException {
-
-
-        // Perform exchange APDU
-        try {
-            byte[] response = mService.exchangeAPDU(handle, data);
-            // Handle potential errors
-            if (response == null) {
-               throw new IOException("Exchange APDU failed");
-            }
-            return response;
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException in exchangeAPDU(): ", e);
-            return null;
-        }
-    }
-
-    public void closeSecureElementConnection(int handle) throws IOException {
-
-        try {
-            int status = mService.closeSecureElementConnection(handle);
-            // Handle potential errors
-            if (ErrorCodes.isError(status)) {
-               throw new IOException("Error during the conection close");
-            };
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException in closeSecureElement(): ", e);
-        }
-    }
-
-
-    /**
-     * Returns target type. constants.
-     *
-     * @return Secure Element technology type. The possible values are defined in
-     * {@link TagTechnology}
-     *
-     */
-    public int[] getSecureElementTechList(int handle) throws IOException {
-        try {
-            return mService.getSecureElementTechList(handle);
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException in getType(): ", e);
-            return null;
-        }
-    }
-
-    /**
-     * Returns Secure Element UID.
-     *
-     * @return Secure Element UID.
-     */
-    public byte[] getSecureElementUid(int handle) throws IOException {
-
-        byte[] uid = null;
-        try {
-            uid = mService.getSecureElementUid(handle);
-            // Handle potential errors
-            if (uid == null) {
-                throw new IOException("Get Secure Element UID failed");
-            }
-            return uid;
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException in getType(): ", e);
-            return null;
-        }
-    }
-
-}
index f56f6a9..632daa1 100644 (file)
@@ -19,19 +19,20 @@ package android.os;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
 import java.util.regex.Pattern;
+import java.util.zip.CRC32;
+import java.util.zip.CheckedInputStream;
 
 
 /**
  * Tools for managing files.  Not for public consumption.
  * @hide
  */
-public class FileUtils
-{
+public class FileUtils {
     public static final int S_IRWXU = 00700;
     public static final int S_IRUSR = 00400;
     public static final int S_IWUSR = 00200;
@@ -94,7 +95,7 @@ public class FileUtils
 
     /** returns the FAT file system volume ID for the volume mounted 
      * at the given mount point, or -1 for failure
-     * @param mount point for FAT volume
+     * @param mountPoint point for FAT volume
      * @return volume ID or -1
      */
     public static native int getFatVolumeId(String mountPoint);
@@ -226,4 +227,32 @@ public class FileUtils
             input.close();
         }
     }
+
+    /**
+     * Computes the checksum of a file using the CRC32 checksum routine.
+     * The value of the checksum is returned.
+     *
+     * @param file  the file to checksum, must not be null
+     * @return the checksum value or an exception is thrown.
+     */
+    public static long checksumCrc32(File file) throws FileNotFoundException, IOException {
+        CRC32 checkSummer = new CRC32();
+        CheckedInputStream cis = null;
+
+        try {
+            cis = new CheckedInputStream( new FileInputStream(file), checkSummer);
+            byte[] buf = new byte[128];
+            while(cis.read(buf) >= 0) {
+                // Just read for checksum to get calculated.
+            }
+            return checkSummer.getValue();
+        } finally {
+            if (cis != null) {
+                try {
+                    cis.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+    }
 }
index 212c5fb..5a245f8 100644 (file)
@@ -19,6 +19,7 @@ package android.os;
 
 import android.net.InterfaceConfiguration;
 import android.net.INetworkManagementEventObserver;
+import android.net.RouteInfo;
 import android.net.wifi.WifiConfiguration;
 
 /**
@@ -57,6 +58,22 @@ interface INetworkManagementService
     void setInterfaceConfig(String iface, in InterfaceConfiguration cfg);
 
     /**
+     * Retrieves the network routes currently configured on the specified
+     * interface
+     */
+    RouteInfo[] getRoutes(String iface);
+
+    /**
+     * Add the specified route to the interface.
+     */
+    void addRoute(String iface, in RouteInfo route);
+
+    /**
+     * Remove the specified route from the interface.
+     */
+    void removeRoute(String iface, in RouteInfo route);
+
+    /**
      * Shuts down the service
      */
     void shutdown();
index 58d9952..eb9eb03 100644 (file)
@@ -3706,6 +3706,29 @@ public final class Settings {
                 "inet_condition_debounce_down_delay";
 
         /**
+         * URL to open browser on to allow user to manage a prepay account
+         * @hide
+         */
+        public static final String SETUP_PREPAID_DATA_SERVICE_URL =
+                "setup_prepaid_data_service_url";
+
+        /**
+         * URL to attempt a GET on to see if this is a prepay device
+         * @hide
+         */
+        public static final String SETUP_PREPAID_DETECTION_TARGET_URL =
+                "setup_prepaid_detection_target_url";
+
+        /**
+         * Host to check for a redirect to after an attempt to GET
+         * SETUP_PREPAID_DETECTION_TARGET_URL. (If we redirected there,
+         * this is a prepaid device with zero balance.)
+         * @hide
+         */
+        public static final String SETUP_PREPAID_DETECTION_REDIR_HOST =
+                "setup_prepaid_detection_redir_host";
+
+        /**
          * @hide
          */
         public static final String[] SETTINGS_TO_BACKUP = {
index 62f66b6..d2d2557 100644 (file)
@@ -1723,6 +1723,14 @@ public final class Telephony {
 
         public static final String TYPE = "type";
 
+        public static final String INACTIVE_TIMER = "inactivetimer";
+
+        // Only if enabled try Data Connection.
+        public static final String ENABLED = "enabled";
+
+        // Rules apply based on class.
+        public static final String CLASS = "class";
+
         /**
          * The protocol to be used to connect to this APN.
          *
index cdb0339..1440a81 100644 (file)
@@ -1204,10 +1204,11 @@ public final class ViewRoot extends Handler implements ViewParent,
                         }
                     }
                     mSurfaceHolder.mSurfaceLock.lock();
-                    // Make surface invalid.
-                    //mSurfaceHolder.mSurface.copyFrom(mSurface);
-                    mSurfaceHolder.mSurface = new Surface();
-                    mSurfaceHolder.mSurfaceLock.unlock();
+                    try {
+                        mSurfaceHolder.mSurface = new Surface();
+                    } finally {
+                        mSurfaceHolder.mSurfaceLock.unlock();
+                    }
                 }
             }
 
index 101dd91..3973344 100644 (file)
@@ -44,16 +44,16 @@ import java.util.Stack;
  * In this usage model there is no need for the destination to
  * use the connect methods. The typical sequence of operations is:</p>
  *<ol>
- *   <li>Client calls AsyncChannel#connect</li>
- *   <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li>
+ *   <li>Client calls AsyncChannel#connectSync or Asynchronously:</li>
+ *      <ol>For an asynchronous half connection client calls AsyncChannel#connect.</ol>
+ *          <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li>
+ *      </ol>
  *   <li><code>comm-loop:</code></li>
- *   <li>Client calls AsyncChannel#sendMessage(msgX)</li>
- *   <li>Server receives and processes msgX</li>
- *   <li>Server optionally calls AsyncChannel#replyToMessage(msgY)
- *       and if sent Client receives and processes msgY</li>
+ *   <li>Client calls AsyncChannel#sendMessage</li>
+ *   <li>Server processes messages and optionally replies using AsyncChannel#replyToMessage
  *   <li>Loop to <code>comm-loop</code> until done</li>
- *   <li>When done Client calls {@link AsyncChannel#disconnect(int)}</li>
- *   <li>Client receives CMD_CHANNEL_DISCONNECTED from AsyncChannel</li>
+ *   <li>When done Client calls {@link AsyncChannel#disconnect}</li>
+ *   <li>Client/Server receives CMD_CHANNEL_DISCONNECTED from AsyncChannel</li>
  *</ol>
  *<br/>
  * <p>A second usage model is where the server/destination needs to know
@@ -62,21 +62,26 @@ import java.util.Stack;
  * different state for each client. In this model the server will also
  * use the connect methods. The typical sequence of operation is:</p>
  *<ol>
- *   <li>Client calls AsyncChannel#connect</li>
- *   <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li>
- *   <li>Client calls AsyncChannel#sendMessage(CMD_CHANNEL_FULL_CONNECTION)</li>
+ *   <li>Client calls AsyncChannel#fullyConnectSync or Asynchronously:<li>
+ *      <ol>For an asynchronous full connection it calls AsyncChannel#connect</li>
+ *          <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li>
+ *          <li>Client calls AsyncChannel#sendMessage(CMD_CHANNEL_FULL_CONNECTION)</li>
+ *      </ol>
  *   <li>Server receives CMD_CHANNEL_FULL_CONNECTION</li>
- *   <li>Server calls AsyncChannel#connect</li>
- *   <li>Server receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li>
+ *   <li>Server calls AsyncChannel#connected</li>
  *   <li>Server sends AsyncChannel#sendMessage(CMD_CHANNEL_FULLY_CONNECTED)</li>
  *   <li>Client receives CMD_CHANNEL_FULLY_CONNECTED</li>
  *   <li><code>comm-loop:</code></li>
  *   <li>Client/Server uses AsyncChannel#sendMessage/replyToMessage
  *       to communicate and perform work</li>
  *   <li>Loop to <code>comm-loop</code> until done</li>
- *   <li>When done Client/Server calls {@link AsyncChannel#disconnect(int)}</li>
+ *   <li>When done Client/Server calls {@link AsyncChannel#disconnect}</li>
  *   <li>Client/Server receives CMD_CHANNEL_DISCONNECTED from AsyncChannel</li>
  *</ol>
+ *
+ * TODO: Consider simplifying where we have connect and fullyConnect with only one response
+ * message RSP_CHANNEL_CONNECT instead of two, CMD_CHANNEL_HALF_CONNECTED and
+ * CMD_CHANNEL_FULLY_CONNECTED. We'd also change CMD_CHANNEL_FULL_CONNECTION to REQ_CHANNEL_CONNECT.
  */
 public class AsyncChannel {
     /** Log tag */
@@ -85,6 +90,8 @@ public class AsyncChannel {
     /** Enable to turn on debugging */
     private static final boolean DBG = false;
 
+    private static final int BASE = Protocol.BASE_SYSTEM_ASYNC_CHANNEL;
+
     /**
      * Command sent when the channel is half connected. Half connected
      * means that the channel can be used to send commends to the destination
@@ -98,7 +105,7 @@ public class AsyncChannel {
      * msg.obj  == the AsyncChannel
      * msg.replyTo == dstMessenger if successful
      */
-    public static final int CMD_CHANNEL_HALF_CONNECTED = -1;
+    public static final int CMD_CHANNEL_HALF_CONNECTED = BASE + 0;
 
     /**
      * Command typically sent when after receiving the CMD_CHANNEL_HALF_CONNECTED.
@@ -107,7 +114,7 @@ public class AsyncChannel {
      *
      * msg.replyTo = srcMessenger.
      */
-    public static final int CMD_CHANNEL_FULL_CONNECTION = -2;
+    public static final int CMD_CHANNEL_FULL_CONNECTION = BASE + 1;
 
     /**
      * Command typically sent after the destination receives a CMD_CHANNEL_FULL_CONNECTION.
@@ -115,31 +122,33 @@ public class AsyncChannel {
      *
      * msg.arg1 == 0 : Accept connection
      *               : All other values signify the destination rejected the connection
-     *                 and {@link AsyncChannel#disconnect(int)} would typically be called.
+     *                 and {@link AsyncChannel#disconnect} would typically be called.
      */
-    public static final int CMD_CHANNEL_FULLY_CONNECTED = -3;
+    public static final int CMD_CHANNEL_FULLY_CONNECTED = BASE + 2;
 
     /**
      * Command sent when one side or the other wishes to disconnect. The sender
      * may or may not be able to receive a reply depending upon the protocol and
-     * the state of the connection. The receiver should call {@link AsyncChannel#disconnect(int)}
+     * the state of the connection. The receiver should call {@link AsyncChannel#disconnect}
      * to close its side of the channel and it will receive a CMD_CHANNEL_DISCONNECTED
      * when the channel is closed.
      *
      * msg.replyTo = messenger that is disconnecting
      */
-    public static final int CMD_CHANNEL_DISCONNECT = -4;
+    public static final int CMD_CHANNEL_DISCONNECT = BASE + 3;
 
     /**
      * Command sent when the channel becomes disconnected. This is sent when the
      * channel is forcibly disconnected by the system or as a reply to CMD_CHANNEL_DISCONNECT.
      *
      * msg.arg1 == 0 : STATUS_SUCCESSFUL
+     *             1 : STATUS_BINDING_UNSUCCESSFUL
+     *             2 : STATUS_SEND_UNSUCCESSFUL
      *               : All other values signify failure and the channel state is indeterminate
      * msg.obj  == the AsyncChannel
      * msg.replyTo = messenger disconnecting or null if it was never connected.
      */
-    public static final int CMD_CHANNEL_DISCONNECTED = -5;
+    public static final int CMD_CHANNEL_DISCONNECTED = BASE + 4;
 
     /** Successful status always 0, !0 is an unsuccessful status */
     public static final int STATUS_SUCCESSFUL = 0;
@@ -147,6 +156,12 @@ public class AsyncChannel {
     /** Error attempting to bind on a connect */
     public static final int STATUS_BINDING_UNSUCCESSFUL = 1;
 
+    /** Error attempting to send a message */
+    public static final int STATUS_SEND_UNSUCCESSFUL = 2;
+
+    /** CMD_FULLY_CONNECTED refused because a connection already exists*/
+    public static final int STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED = 3;
+
     /** Service connection */
     private AsyncChannelConnection mConnection;
 
@@ -169,9 +184,7 @@ public class AsyncChannel {
     }
 
     /**
-     * Connect handler to named package/class.
-     *
-     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
+     * Connect handler to named package/class synchronously.
      *
      * @param srcContext is the context of the source
      * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
@@ -179,8 +192,10 @@ public class AsyncChannel {
      * @param dstPackageName is the destination package name
      * @param dstClassName is the fully qualified class name (i.e. contains
      *            package name)
+     *
+     * @return STATUS_SUCCESSFUL on success any other value is an error.
      */
-    private void connectSrcHandlerToPackage(
+    public int connectSrcHandlerToPackageSync(
             Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName) {
         if (DBG) log("connect srcHandler to dst Package & class E");
 
@@ -202,11 +217,61 @@ public class AsyncChannel {
         Intent intent = new Intent(Intent.ACTION_MAIN);
         intent.setClassName(dstPackageName, dstClassName);
         boolean result = srcContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
-        if (!result) {
-            replyHalfConnected(STATUS_BINDING_UNSUCCESSFUL);
-        }
-
         if (DBG) log("connect srcHandler to dst Package & class X result=" + result);
+        return result ? STATUS_SUCCESSFUL : STATUS_BINDING_UNSUCCESSFUL;
+    }
+
+    /**
+     * Connect a handler to Messenger synchronously.
+     *
+     * @param srcContext is the context of the source
+     * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
+     *            messages
+     * @param dstMessenger is the hander to send messages to.
+     *
+     * @return STATUS_SUCCESSFUL on success any other value is an error.
+     */
+    public int connectSync(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
+        if (DBG) log("halfConnectSync srcHandler to the dstMessenger  E");
+
+        // We are connected
+        connected(srcContext, srcHandler, dstMessenger);
+
+        if (DBG) log("halfConnectSync srcHandler to the dstMessenger X");
+        return STATUS_SUCCESSFUL;
+    }
+
+    /**
+     * connect two local Handlers synchronously.
+     *
+     * @param srcContext is the context of the source
+     * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
+     *            messages
+     * @param dstHandler is the hander to send messages to.
+     *
+     * @return STATUS_SUCCESSFUL on success any other value is an error.
+     */
+    public int connectSync(Context srcContext, Handler srcHandler, Handler dstHandler) {
+        return connectSync(srcContext, srcHandler, new Messenger(dstHandler));
+    }
+
+    /**
+     * Fully connect two local Handlers synchronously.
+     *
+     * @param srcContext is the context of the source
+     * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
+     *            messages
+     * @param dstHandler is the hander to send messages to.
+     *
+     * @return STATUS_SUCCESSFUL on success any other value is an error.
+     */
+    public int fullyConnectSync(Context srcContext, Handler srcHandler, Handler dstHandler) {
+        int status = connectSync(srcContext, srcHandler, dstHandler);
+        if (status == STATUS_SUCCESSFUL) {
+            Message response = sendMessageSynchronously(CMD_CHANNEL_FULL_CONNECTION);
+            status = response.arg1;
+        }
+        return status;
     }
 
     /**
@@ -241,8 +306,11 @@ public class AsyncChannel {
                 mDstClassName = dstClassName;
             }
 
+            @Override
             public void run() {
-                connectSrcHandlerToPackage(mSrcCtx, mSrcHdlr, mDstPackageName, mDstClassName);
+                int result = connectSrcHandlerToPackageSync(mSrcCtx, mSrcHdlr, mDstPackageName,
+                        mDstClassName);
+                replyHalfConnected(result);
             }
         }
 
@@ -281,6 +349,28 @@ public class AsyncChannel {
     public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
         if (DBG) log("connect srcHandler to the dstMessenger  E");
 
+        // We are connected
+        connected(srcContext, srcHandler, dstMessenger);
+
+        // Tell source we are half connected
+        replyHalfConnected(STATUS_SUCCESSFUL);
+
+        if (DBG) log("connect srcHandler to the dstMessenger X");
+    }
+
+    /**
+     * Connect handler to messenger. This method is typically called
+     * when a server receives a CMD_CHANNEL_FULL_CONNECTION request
+     * and initializes the internal instance variables to allow communication
+     * with the dstMessenger.
+     *
+     * @param srcContext
+     * @param srcHandler
+     * @param dstMessenger
+     */
+    public void connected(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
+        if (DBG) log("connected srcHandler to the dstMessenger  E");
+
         // Initialize source fields
         mSrcContext = srcContext;
         mSrcHandler = srcHandler;
@@ -289,21 +379,12 @@ public class AsyncChannel {
         // Initialize destination fields
         mDstMessenger = dstMessenger;
 
-        if (DBG) log("tell source we are half connected");
-
-        // Tell source we are half connected
-        replyHalfConnected(STATUS_SUCCESSFUL);
-
-        if (DBG) log("connect srcHandler to the dstMessenger X");
+        if (DBG) log("connected srcHandler to the dstMessenger X");
     }
 
     /**
      * Connect two local Handlers.
      *
-     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
-     *      msg.arg1 = status
-     *      msg.obj = the AsyncChannel
-     *
      * @param srcContext is the context of the source
      * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
      *            messages
@@ -331,6 +412,7 @@ public class AsyncChannel {
      * To close the connection call when handler receives CMD_CHANNEL_DISCONNECTED
      */
     public void disconnected() {
+        mSrcContext = null;
         mSrcHandler = null;
         mSrcMessenger = null;
         mDstMessenger = null;
@@ -341,15 +423,11 @@ public class AsyncChannel {
      * Disconnect
      */
     public void disconnect() {
-        if (mConnection != null) {
+        if ((mConnection != null) && (mSrcContext != null)) {
             mSrcContext.unbindService(mConnection);
         }
         if (mSrcHandler != null) {
-            Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
-            msg.arg1 = STATUS_SUCCESSFUL;
-            msg.obj = this;
-            msg.replyTo = mDstMessenger;
-            mSrcHandler.sendMessage(msg);
+            replyDisconnected(STATUS_SUCCESSFUL);
         }
     }
 
@@ -363,7 +441,7 @@ public class AsyncChannel {
         try {
             mDstMessenger.send(msg);
         } catch (RemoteException e) {
-            log("TODO: handle sendMessage RemoteException" + e);
+            replyDisconnected(STATUS_SEND_UNSUCCESSFUL);
         }
     }
 
@@ -444,6 +522,7 @@ public class AsyncChannel {
      */
     public void replyToMessage(Message srcMsg, Message dstMsg) {
         try {
+            dstMsg.replyTo = mSrcMessenger;
             srcMsg.replyTo.send(dstMsg);
         } catch (RemoteException e) {
             log("TODO: handle replyToMessage RemoteException" + e);
@@ -694,10 +773,14 @@ public class AsyncChannel {
         private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) {
             SyncMessenger sm = SyncMessenger.obtain();
             try {
-                msg.replyTo = sm.mMessenger;
-                dstMessenger.send(msg);
-                synchronized (sm.mHandler.mLockObject) {
-                    sm.mHandler.mLockObject.wait();
+                if (dstMessenger != null && msg != null) {
+                    msg.replyTo = sm.mMessenger;
+                    synchronized (sm.mHandler.mLockObject) {
+                        dstMessenger.send(msg);
+                        sm.mHandler.mLockObject.wait();
+                    }
+                } else {
+                    sm.mHandler.mResultMsg = null;
                 }
             } catch (InterruptedException e) {
                 sm.mHandler.mResultMsg = null;
@@ -712,6 +795,7 @@ public class AsyncChannel {
 
     /**
      * Reply to the src handler that we're half connected.
+     * see: CMD_CHANNEL_HALF_CONNECTED for message contents
      *
      * @param status to be stored in msg.arg1
      */
@@ -724,23 +808,36 @@ public class AsyncChannel {
     }
 
     /**
+     * Reply to the src handler that we are disconnected
+     * see: CMD_CHANNEL_DISCONNECTED for message contents
+     *
+     * @param status to be stored in msg.arg1
+     */
+    private void replyDisconnected(int status) {
+        Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
+        msg.arg1 = status;
+        msg.obj = this;
+        msg.replyTo = mDstMessenger;
+        mSrcHandler.sendMessage(msg);
+    }
+
+
+    /**
      * ServiceConnection to receive call backs.
      */
     class AsyncChannelConnection implements ServiceConnection {
         AsyncChannelConnection() {
         }
 
+        @Override
         public void onServiceConnected(ComponentName className, IBinder service) {
             mDstMessenger = new Messenger(service);
             replyHalfConnected(STATUS_SUCCESSFUL);
         }
 
+        @Override
         public void onServiceDisconnected(ComponentName className) {
-            Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
-            msg.arg1 = STATUS_SUCCESSFUL;
-            msg.obj = AsyncChannel.this;
-            msg.replyTo = mDstMessenger;
-            mSrcHandler.sendMessage(msg);
+            replyDisconnected(STATUS_SUCCESSFUL);
         }
     }
 
@@ -1,5 +1,5 @@
 /**
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2011 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.
@@ -21,21 +21,29 @@ import android.os.Message;
 /**
  * {@hide}
  *
- * The class for implementing states in a HierarchicalStateMachine
+ * The interface for implementing states in a {@link StateMachine}
  */
-public class HierarchicalState {
+public interface IState {
 
     /**
-     * Constructor
+     * Returned by processMessage to indicate the the message was processed.
      */
-    protected HierarchicalState() {
-    }
+    static final boolean HANDLED = true;
+
+    /**
+     * Returned by processMessage to indicate the the message was NOT processed.
+     */
+    static final boolean NOT_HANDLED = false;
 
     /**
      * Called when a state is entered.
      */
-    protected void enter() {
-    }
+    void enter();
+
+    /**
+     * Called when a state is exited.
+     */
+    void exit();
 
     /**
      * Called when a message is to be processed by the
@@ -49,28 +57,15 @@ public class HierarchicalState {
      * be processed until this routine returns.
      *
      * @param msg to process
-     * @return true if processing has completed and false
-     *         if the parent state's processMessage should
-     *         be invoked.
-     */
-    protected boolean processMessage(Message msg) {
-        return false;
-    }
-
-    /**
-     * Called when a state is exited.
+     * @return HANDLED if processing has completed and NOT_HANDLED
+     *         if the message wasn't processed.
      */
-    protected void exit() {
-    }
+    boolean processMessage(Message msg);
 
     /**
-     * @return name of state, but default returns the states
-     * class name. An instance name would be better but requiring
-     * it seems unnecessary.
+     * Name of State for debugging purposes.
+     *
+     * @return name of state.
      */
-    public String getName() {
-        String name = getClass().getName();
-        int lastDollar = name.lastIndexOf('$');
-        return name.substring(lastDollar + 1);
-    }
+    String getName();
 }
diff --git a/core/java/com/android/internal/util/ProcessedMessages.java b/core/java/com/android/internal/util/ProcessedMessages.java
deleted file mode 100644 (file)
index 244474e..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-/**
- * Copyright (C) 2009 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.util;
-
-import android.os.Message;
-
-import java.util.Vector;
-
-/**
- * {@hide}
- *
- * A list of messages recently processed by the state machine.
- *
- * The class maintains a list of messages that have been most
- * recently processed. The list is finite and may be set in the
- * constructor or by calling setSize. The public interface also
- * includes size which returns the number of recent messages,
- * count which is the number of message processed since the
- * the last setSize, get which returns a processed message and
- * add which adds a processed messaged.
- */
-public class ProcessedMessages {
-
-    public static final int DEFAULT_SIZE = 20;
-
-    /**
-     * The information maintained for a processed message.
-     */
-    public class Info {
-        private int what;
-        private HierarchicalState state;
-        private HierarchicalState orgState;
-
-        /**
-         * Constructor
-         * @param message
-         * @param state that handled the message
-         * @param orgState is the first state the received the message but
-         * did not processes the message.
-         */
-        Info(Message message, HierarchicalState state, HierarchicalState orgState) {
-            update(message, state, orgState);
-        }
-
-        /**
-         * Update the information in the record.
-         * @param state that handled the message
-         * @param orgState is the first state the received the message but
-         * did not processes the message.
-         */
-        public void update(Message message, HierarchicalState state, HierarchicalState orgState) {
-            this.what = message.what;
-            this.state = state;
-            this.orgState = orgState;
-        }
-
-        /**
-         * @return the command that was executing
-         */
-        public int getWhat() {
-            return what;
-        }
-
-        /**
-         * @return the state that handled this message
-         */
-        public HierarchicalState getState() {
-            return state;
-        }
-
-        /**
-         * @return the original state that received the message.
-         */
-        public HierarchicalState getOriginalState() {
-            return orgState;
-        }
-
-        /**
-         * @return as string
-         */
-        public String toString() {
-            StringBuilder sb = new StringBuilder();
-            sb.append("what=");
-            sb.append(what);
-            sb.append(" state=");
-            sb.append(cn(state));
-            sb.append(" orgState=");
-            sb.append(cn(orgState));
-            return sb.toString();
-        }
-
-        /**
-         * @return an objects class name
-         */
-        private String cn(Object n) {
-            if (n == null) {
-                return "null";
-            } else {
-                String name = n.getClass().getName();
-                int lastDollar = name.lastIndexOf('$');
-                return name.substring(lastDollar + 1);
-            }
-        }
-    }
-
-    private Vector<Info> mMessages = new Vector<Info>();
-    private int mMaxSize = DEFAULT_SIZE;
-    private int mOldestIndex = 0;
-    private int mCount = 0;
-
-    /**
-     * Constructor
-     */
-    ProcessedMessages() {
-    }
-
-    ProcessedMessages(int maxSize) {
-        setSize(maxSize);
-    }
-
-    /**
-     * Set size of messages to maintain and clears all current messages.
-     *
-     * @param maxSize number of messages to maintain at anyone time.
-    */
-    void setSize(int maxSize) {
-        mMaxSize = maxSize;
-        mCount = 0;
-        mMessages.clear();
-    }
-
-    /**
-     * @return the number of recent messages.
-     */
-    int size() {
-        return mMessages.size();
-    }
-
-    /**
-     * @return the total number of messages processed since size was set.
-     */
-    int count() {
-        return mCount;
-    }
-
-    /**
-     * @return the information on a particular record. 0 is the oldest
-     * record and size()-1 is the newest record. If the index is to
-     * large null is returned.
-     */
-    Info get(int index) {
-        int nextIndex = mOldestIndex + index;
-        if (nextIndex >= mMaxSize) {
-            nextIndex -= mMaxSize;
-        }
-        if (nextIndex >= size()) {
-            return null;
-        } else {
-            return mMessages.get(nextIndex);
-        }
-    }
-
-    /**
-     * Add a processed message.
-     *
-     * @param message
-     * @param state that handled the message
-     * @param orgState is the first state the received the message but
-     * did not processes the message.
-     */
-    void add(Message message, HierarchicalState state, HierarchicalState orgState) {
-        mCount += 1;
-        if (mMessages.size() < mMaxSize) {
-            mMessages.add(new Info(message, state, orgState));
-        } else {
-            Info info = mMessages.get(mOldestIndex);
-            mOldestIndex += 1;
-            if (mOldestIndex >= mMaxSize) {
-                mOldestIndex = 0;
-            }
-            info.update(message, state, orgState);
-        }
-    }
-}
index 2689f09..b754d94 100644 (file)
@@ -26,12 +26,24 @@ package com.android.internal.util;
  * codes with Message.what starting at Protocol.WIFI + 1 and less than or equal to Protocol.WIFI +
  * Protocol.MAX_MESSAGE
  *
+ * NOTE: After a value is created and source released a value shouldn't be changed to
+ * maintain backwards compatibility.
+ *
  * {@hide}
  */
 public class Protocol {
-    public static final int MAX_MESSAGE     =  0x0000FFFF;
+    public static final int MAX_MESSAGE                                             = 0x0000FFFF;
+
+    /** Base reserved for system */
+    public static final int BASE_SYSTEM_RESERVED                                    = 0x00010000;
+    public static final int BASE_SYSTEM_ASYNC_CHANNEL                               = 0x00011000;
+
+    /** Non system protocols */
+    public static final int BASE_WIFI                                               = 0x00020000;
+    public static final int BASE_DHCP                                               = 0x00030000;
+    public static final int BASE_DATA_CONNECTION                                    = 0x00040000;
+    public static final int BASE_DATA_CONNECTION_AC                                 = 0x00041000;
+    public static final int BASE_DATA_CONNECTION_TRACKER                            = 0x00042000;
 
-    public static final int BASE_WIFI       =  0x00010000;
-    public static final int BASE_DHCP       =  0x00020000;
     //TODO: define all used protocols
 }
diff --git a/core/java/com/android/internal/util/State.java b/core/java/com/android/internal/util/State.java
new file mode 100644 (file)
index 0000000..3eadff5
--- /dev/null
@@ -0,0 +1,74 @@
+/**
+ * Copyright (C) 2009 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.util;
+
+import android.os.Message;
+
+/**
+ * {@hide}
+ *
+ * The class for implementing states in a StateMachine
+ */
+public class State implements IState {
+
+    /**
+     * Constructor
+     */
+    protected State() {
+    }
+
+    /* (non-Javadoc)
+     * @see com.android.internal.util.IState#enter()
+     */
+    @Override
+    public void enter() {
+    }
+
+    /* (non-Javadoc)
+     * @see com.android.internal.util.IState#exit()
+     */
+    @Override
+    public void exit() {
+    }
+
+    /* (non-Javadoc)
+     * @see com.android.internal.util.IState#processMessage(android.os.Message)
+     */
+    @Override
+    public boolean processMessage(Message msg) {
+        return false;
+    }
+
+    /**
+     * Name of State for debugging purposes.
+     *
+     * This default implementation returns the class name, returning
+     * the instance name would better in cases where a State class
+     * is used for multiple states. But normally there is one class per
+     * state and the class name is sufficient and easy to get. You may
+     * want to provide a setName or some other mechanism for setting
+     * another name if the class name is not appropriate.
+     *
+     * @see com.android.internal.util.IState#processMessage(android.os.Message)
+     */
+    @Override
+    public String getName() {
+        String name = getClass().getName();
+        int lastDollar = name.lastIndexOf('$');
+        return name.substring(lastDollar + 1);
+    }
+}
@@ -24,14 +24,15 @@ import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Vector;
 
 /**
  * {@hide}
  *
- * <p>A hierarchical state machine is a state machine which processes messages
+ * <p>The state machine defined here is a hierarchical state machine which processes messages
  * and can have states arranged hierarchically.</p>
  * 
- * <p>A state is a <code>HierarchicalState</code> object and must implement
+ * <p>A state is a <code>State</code> object and must implement
  * <code>processMessage</code> and optionally <code>enter/exit/getName</code>.
  * The enter/exit methods are equivalent to the construction and destruction
  * in Object Oriented programming and are used to perform initialization and
@@ -75,7 +76,7 @@ import java.util.HashMap;
  * will exit the current state and its parent and then exit from the controlling thread
  * and no further messages will be processed.</p>
  *
- * <p>In addition to <code>processMessage</code> each <code>HierarchicalState</code> has
+ * <p>In addition to <code>processMessage</code> each <code>State</code> has
  * an <code>enter</code> method and <code>exit</exit> method which may be overridden.</p>
  *
  * <p>Since the states are arranged in a hierarchy transitioning to a new state
@@ -121,11 +122,11 @@ import java.util.HashMap;
  * mS4.enter. The new list of active states is mP0, mP1, mS2 and mS4. So
  * when the next message is received mS4.processMessage will be invoked.</p>
  *
- * <p>Now for some concrete examples, here is the canonical HelloWorld as an HSM.
+ * <p>Now for some concrete examples, here is the canonical HelloWorld as a state machine.
  * It responds with "Hello World" being printed to the log for every message.</p>
 <code>
-class HelloWorld extends HierarchicalStateMachine {
-    Hsm1(String name) {
+class HelloWorld extends StateMachine {
+    HelloWorld(String name) {
         super(name);
         addState(mState1);
         setInitialState(mState1);
@@ -137,7 +138,7 @@ class HelloWorld extends HierarchicalStateMachine {
         return hw;
     }
 
-    class State1 extends HierarchicalState {
+    class State1 extends State {
         &#64;Override public boolean processMessage(Message message) {
             Log.d(TAG, "Hello World");
             return HANDLED;
@@ -220,9 +221,9 @@ state mP2 {
      }
 }
 </code>
- * <p>The implementation is below and also in HierarchicalStateMachineTest:</p>
+ * <p>The implementation is below and also in StateMachineTest:</p>
 <code>
-class Hsm1 extends HierarchicalStateMachine {
+class Hsm1 extends StateMachine {
     private static final String TAG = "hsm1";
 
     public static final int CMD_1 = 1;
@@ -254,7 +255,7 @@ class Hsm1 extends HierarchicalStateMachine {
         Log.d(TAG, "ctor X");
     }
 
-    class P1 extends HierarchicalState {
+    class P1 extends State {
         &#64;Override public void enter() {
             Log.d(TAG, "mP1.enter");
         }
@@ -281,7 +282,7 @@ class Hsm1 extends HierarchicalStateMachine {
         }
     }
 
-    class S1 extends HierarchicalState {
+    class S1 extends State {
         &#64;Override public void enter() {
             Log.d(TAG, "mS1.enter");
         }
@@ -301,7 +302,7 @@ class Hsm1 extends HierarchicalStateMachine {
         }
     }
 
-    class S2 extends HierarchicalState {
+    class S2 extends State {
         &#64;Override public void enter() {
             Log.d(TAG, "mS2.enter");
         }
@@ -329,7 +330,7 @@ class Hsm1 extends HierarchicalStateMachine {
         }
     }
 
-    class P2 extends HierarchicalState {
+    class P2 extends State {
         &#64;Override public void enter() {
             Log.d(TAG, "mP2.enter");
             sendMessage(obtainMessage(CMD_5));
@@ -408,16 +409,16 @@ D/hsm1    ( 1999): mP2.exit
 D/hsm1    ( 1999): halting
 </code>
  */
-public class HierarchicalStateMachine {
+public class StateMachine {
 
-    private static final String TAG = "HierarchicalStateMachine";
+    private static final String TAG = "StateMachine";
     private String mName;
 
     /** Message.what value when quitting */
-    public static final int HSM_QUIT_CMD = -1;
+    public static final int SM_QUIT_CMD = -1;
 
     /** Message.what value when initializing */
-    public static final int HSM_INIT_CMD = -1;
+    public static final int SM_INIT_CMD = -1;
 
     /**
      * Convenience constant that maybe returned by processMessage
@@ -433,7 +434,181 @@ public class HierarchicalStateMachine {
      */
     public static final boolean NOT_HANDLED = false;
 
-    private static class HsmHandler extends Handler {
+    /**
+     * {@hide}
+     *
+     * The information maintained for a processed message.
+     */
+    public static class ProcessedMessageInfo {
+        private int what;
+        private State state;
+        private State orgState;
+
+        /**
+         * Constructor
+         * @param message
+         * @param state that handled the message
+         * @param orgState is the first state the received the message but
+         * did not processes the message.
+         */
+        ProcessedMessageInfo(Message message, State state, State orgState) {
+            update(message, state, orgState);
+        }
+
+        /**
+         * Update the information in the record.
+         * @param state that handled the message
+         * @param orgState is the first state the received the message but
+         * did not processes the message.
+         */
+        public void update(Message message, State state, State orgState) {
+            this.what = message.what;
+            this.state = state;
+            this.orgState = orgState;
+        }
+
+        /**
+         * @return the command that was executing
+         */
+        public int getWhat() {
+            return what;
+        }
+
+        /**
+         * @return the state that handled this message
+         */
+        public State getState() {
+            return state;
+        }
+
+        /**
+         * @return the original state that received the message.
+         */
+        public State getOriginalState() {
+            return orgState;
+        }
+
+        /**
+         * @return as string
+         */
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("what=");
+            sb.append(what);
+            sb.append(" state=");
+            sb.append(cn(state));
+            sb.append(" orgState=");
+            sb.append(cn(orgState));
+            return sb.toString();
+        }
+
+        /**
+         * @return an objects class name
+         */
+        private String cn(Object n) {
+            if (n == null) {
+                return "null";
+            } else {
+                String name = n.getClass().getName();
+                int lastDollar = name.lastIndexOf('$');
+                return name.substring(lastDollar + 1);
+            }
+        }
+    }
+
+    /**
+     * A list of messages recently processed by the state machine.
+     *
+     * The class maintains a list of messages that have been most
+     * recently processed. The list is finite and may be set in the
+     * constructor or by calling setSize. The public interface also
+     * includes size which returns the number of recent messages,
+     * count which is the number of message processed since the
+     * the last setSize, get which returns a processed message and
+     * add which adds a processed messaged.
+     */
+    private static class ProcessedMessages {
+
+        private static final int DEFAULT_SIZE = 20;
+
+        private Vector<ProcessedMessageInfo> mMessages = new Vector<ProcessedMessageInfo>();
+        private int mMaxSize = DEFAULT_SIZE;
+        private int mOldestIndex = 0;
+        private int mCount = 0;
+
+        /**
+         * Constructor
+         */
+        ProcessedMessages() {
+        }
+
+        /**
+         * Set size of messages to maintain and clears all current messages.
+         *
+         * @param maxSize number of messages to maintain at anyone time.
+        */
+        void setSize(int maxSize) {
+            mMaxSize = maxSize;
+            mCount = 0;
+            mMessages.clear();
+        }
+
+        /**
+         * @return the number of recent messages.
+         */
+        int size() {
+            return mMessages.size();
+        }
+
+        /**
+         * @return the total number of messages processed since size was set.
+         */
+        int count() {
+            return mCount;
+        }
+
+        /**
+         * @return the information on a particular record. 0 is the oldest
+         * record and size()-1 is the newest record. If the index is to
+         * large null is returned.
+         */
+        ProcessedMessageInfo get(int index) {
+            int nextIndex = mOldestIndex + index;
+            if (nextIndex >= mMaxSize) {
+                nextIndex -= mMaxSize;
+            }
+            if (nextIndex >= size()) {
+                return null;
+            } else {
+                return mMessages.get(nextIndex);
+            }
+        }
+
+        /**
+         * Add a processed message.
+         *
+         * @param message
+         * @param state that handled the message
+         * @param orgState is the first state the received the message but
+         * did not processes the message.
+         */
+        void add(Message message, State state, State orgState) {
+            mCount += 1;
+            if (mMessages.size() < mMaxSize) {
+                mMessages.add(new ProcessedMessageInfo(message, state, orgState));
+            } else {
+                ProcessedMessageInfo pmi = mMessages.get(mOldestIndex);
+                mOldestIndex += 1;
+                if (mOldestIndex >= mMaxSize) {
+                    mOldestIndex = 0;
+                }
+                pmi.update(message, state, orgState);
+            }
+        }
+    }
+
+    private static class SmHandler extends Handler {
 
         /** The debug flag */
         private boolean mDbg = false;
@@ -441,9 +616,6 @@ public class HierarchicalStateMachine {
         /** The quit object */
         private static final Object mQuitObj = new Object();
 
-        /** The initialization message */
-        private static final Message mInitMsg = null;
-
         /** The current message */
         private Message mMsg;
 
@@ -471,8 +643,8 @@ public class HierarchicalStateMachine {
         /** State used when state machine is quitting */
         private QuittingState mQuittingState = new QuittingState();
 
-        /** Reference to the HierarchicalStateMachine */
-        private HierarchicalStateMachine mHsm;
+        /** Reference to the StateMachine */
+        private StateMachine mSm;
 
         /**
          * Information about a state.
@@ -480,7 +652,7 @@ public class HierarchicalStateMachine {
          */
         private class StateInfo {
             /** The state */
-            HierarchicalState state;
+            State state;
 
             /** The parent of this state, null if there is no parent */
             StateInfo parentStateInfo;
@@ -500,14 +672,14 @@ public class HierarchicalStateMachine {
         }
 
         /** The map of all of the states in the state machine */
-        private HashMap<HierarchicalState, StateInfo> mStateInfo =
-            new HashMap<HierarchicalState, StateInfo>();
+        private HashMap<State, StateInfo> mStateInfo =
+            new HashMap<State, StateInfo>();
 
         /** The initial state that will process the first message */
-        private HierarchicalState mInitialState;
+        private State mInitialState;
 
         /** The destination state when transitionTo has been invoked */
-        private HierarchicalState mDestState;
+        private State mDestState;
 
         /** The list of deferred messages */
         private ArrayList<Message> mDeferredMessages = new ArrayList<Message>();
@@ -515,10 +687,10 @@ public class HierarchicalStateMachine {
         /**
          * State entered when transitionToHaltingState is called.
          */
-        private class HaltingState extends HierarchicalState {
+        private class HaltingState extends State {
             @Override
             public boolean processMessage(Message msg) {
-                mHsm.haltedProcessMessage(msg);
+                mSm.haltedProcessMessage(msg);
                 return true;
             }
         }
@@ -526,7 +698,7 @@ public class HierarchicalStateMachine {
         /**
          * State entered when a valid quit message is handled.
          */
-        private class QuittingState extends HierarchicalState {
+        private class QuittingState extends State {
             @Override
             public boolean processMessage(Message msg) {
                 return NOT_HANDLED;
@@ -573,7 +745,7 @@ public class HierarchicalStateMachine {
              * the appropriate states. We loop on this to allow
              * enter and exit methods to use transitionTo.
              */
-            HierarchicalState destState = null;
+            State destState = null;
             while (mDestState != null) {
                 if (mDbg) Log.d(TAG, "handleMessage: new destination call exit");
 
@@ -613,10 +785,11 @@ public class HierarchicalStateMachine {
                     /**
                      * We are quitting so ignore all messages.
                      */
-                    mHsm.quitting();
-                    if (mHsm.mHsmThread != null) {
-                        // If we made the thread then quit looper
+                    mSm.quitting();
+                    if (mSm.mSmThread != null) {
+                        // If we made the thread then quit looper which stops the thread.
                         getLooper().quit();
+                        mSm.mSmThread = null;
                     }
                 } else if (destState == mHaltingState) {
                     /**
@@ -624,7 +797,7 @@ public class HierarchicalStateMachine {
                      * state. All subsequent messages will be processed in
                      * in the halting state which invokes haltedProcessMessage(msg);
                      */
-                    mHsm.halting();
+                    mSm.halting();
                 }
             }
         }
@@ -660,7 +833,7 @@ public class HierarchicalStateMachine {
              * starting at the first entry.
              */
             mIsConstructionCompleted = true;
-            mMsg = obtainMessage(HSM_INIT_CMD);
+            mMsg = obtainMessage(SM_INIT_CMD);
             invokeEnterMethods(0);
 
             /**
@@ -690,7 +863,7 @@ public class HierarchicalStateMachine {
                     /**
                      * No parents left so it's not handled
                      */
-                    mHsm.unhandledMessage(msg);
+                    mSm.unhandledMessage(msg);
                     if (isQuit(msg)) {
                         transitionTo(mQuittingState);
                     }
@@ -705,7 +878,7 @@ public class HierarchicalStateMachine {
              * Record that we processed the message
              */
             if (curStateInfo != null) {
-                HierarchicalState orgState = mStateStack[mStateStackTopIndex].state;
+                State orgState = mStateStack[mStateStackTopIndex].state;
                 mProcessedMessages.add(msg, curStateInfo.state, orgState);
             } else {
                 mProcessedMessages.add(msg, null, null);
@@ -719,7 +892,7 @@ public class HierarchicalStateMachine {
         private final void invokeExitMethods(StateInfo commonStateInfo) {
             while ((mStateStackTopIndex >= 0) &&
                     (mStateStack[mStateStackTopIndex] != commonStateInfo)) {
-                HierarchicalState curState = mStateStack[mStateStackTopIndex].state;
+                State curState = mStateStack[mStateStackTopIndex].state;
                 if (mDbg) Log.d(TAG, "invokeExitMethods: " + curState.getName());
                 curState.exit();
                 mStateStack[mStateStackTopIndex].active = false;
@@ -761,7 +934,7 @@ public class HierarchicalStateMachine {
          * reversing the order of the items on the temporary stack as
          * they are moved.
          *
-         * @return index into mStateState where entering needs to start
+         * @return index into mStateStack where entering needs to start
          */
         private final int moveTempStateStackToStateStack() {
             int startingIndex = mStateStackTopIndex + 1;
@@ -794,7 +967,7 @@ public class HierarchicalStateMachine {
          * @return StateInfo of the common ancestor for the destState and
          * current state or null if there is no common parent.
          */
-        private final StateInfo setupTempStateStackWithStatesToEnter(HierarchicalState destState) {
+        private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {
             /**
              * Search up the parent list of the destination state for an active
              * state. Use a do while() loop as the destState must always be entered
@@ -846,7 +1019,7 @@ public class HierarchicalStateMachine {
         /**
          * @return current state
          */
-        private final HierarchicalState getCurrentState() {
+        private final IState getCurrentState() {
             return mStateStack[mStateStackTopIndex].state;
         }
 
@@ -859,7 +1032,7 @@ public class HierarchicalStateMachine {
          * @param parent the parent of state
          * @return stateInfo for this state
          */
-        private final StateInfo addState(HierarchicalState state, HierarchicalState parent) {
+        private final StateInfo addState(State state, State parent) {
             if (mDbg) {
                 Log.d(TAG, "addStateInternal: E state=" + state.getName()
                         + ",parent=" + ((parent == null) ? "" : parent.getName()));
@@ -894,29 +1067,29 @@ public class HierarchicalStateMachine {
          * Constructor
          *
          * @param looper for dispatching messages
-         * @param hsm the hierarchical state machine
+         * @param sm the hierarchical state machine
          */
-        private HsmHandler(Looper looper, HierarchicalStateMachine hsm) {
+        private SmHandler(Looper looper, StateMachine sm) {
             super(looper);
-            mHsm = hsm;
+            mSm = sm;
 
             addState(mHaltingState, null);
             addState(mQuittingState, null);
         }
 
-        /** @see HierarchicalStateMachine#setInitialState(HierarchicalState) */
-        private final void setInitialState(HierarchicalState initialState) {
+        /** @see StateMachine#setInitialState(State) */
+        private final void setInitialState(State initialState) {
             if (mDbg) Log.d(TAG, "setInitialState: initialState" + initialState.getName());
             mInitialState = initialState;
         }
 
-        /** @see HierarchicalStateMachine#transitionTo(HierarchicalState) */
-        private final void transitionTo(HierarchicalState destState) {
-            if (mDbg) Log.d(TAG, "StateMachine.transitionTo EX destState" + destState.getName());
-            mDestState = destState;
+        /** @see StateMachine#transitionTo(IState) */
+        private final void transitionTo(IState destState) {
+            mDestState = (State) destState;
+            if (mDbg) Log.d(TAG, "StateMachine.transitionTo EX destState" + mDestState.getName());
         }
 
-        /** @see HierarchicalStateMachine#deferMessage(Message) */
+        /** @see StateMachine#deferMessage(Message) */
         private final void deferMessage(Message msg) {
             if (mDbg) Log.d(TAG, "deferMessage: msg=" + msg.what);
 
@@ -927,51 +1100,51 @@ public class HierarchicalStateMachine {
             mDeferredMessages.add(newMsg);
         }
 
-        /** @see HierarchicalStateMachine#deferMessage(Message) */
+        /** @see StateMachine#deferMessage(Message) */
         private final void quit() {
             if (mDbg) Log.d(TAG, "quit:");
-            sendMessage(obtainMessage(HSM_QUIT_CMD, mQuitObj));
+            sendMessage(obtainMessage(SM_QUIT_CMD, mQuitObj));
         }
 
-        /** @see HierarchicalStateMachine#isQuit(Message) */
+        /** @see StateMachine#isQuit(Message) */
         private final boolean isQuit(Message msg) {
-            return (msg.what == HSM_QUIT_CMD) && (msg.obj == mQuitObj);
+            return (msg.what == SM_QUIT_CMD) && (msg.obj == mQuitObj);
         }
 
-        /** @see HierarchicalStateMachine#isDbg() */
+        /** @see StateMachine#isDbg() */
         private final boolean isDbg() {
             return mDbg;
         }
 
-        /** @see HierarchicalStateMachine#setDbg(boolean) */
+        /** @see StateMachine#setDbg(boolean) */
         private final void setDbg(boolean dbg) {
             mDbg = dbg;
         }
 
-        /** @see HierarchicalStateMachine#setProcessedMessagesSize(int) */
+        /** @see StateMachine#setProcessedMessagesSize(int) */
         private final void setProcessedMessagesSize(int maxSize) {
             mProcessedMessages.setSize(maxSize);
         }
 
-        /** @see HierarchicalStateMachine#getProcessedMessagesSize() */
+        /** @see StateMachine#getProcessedMessagesSize() */
         private final int getProcessedMessagesSize() {
             return mProcessedMessages.size();
         }
 
-        /** @see HierarchicalStateMachine#getProcessedMessagesCount() */
+        /** @see StateMachine#getProcessedMessagesCount() */
         private final int getProcessedMessagesCount() {
             return mProcessedMessages.count();
         }
 
-        /** @see HierarchicalStateMachine#getProcessedMessage(int) */
-        private final ProcessedMessages.Info getProcessedMessage(int index) {
+        /** @see StateMachine#getProcessedMessageInfo(int) */
+        private final ProcessedMessageInfo getProcessedMessageInfo(int index) {
             return mProcessedMessages.get(index);
         }
 
     }
 
-    private HsmHandler mHsmHandler;
-    private HandlerThread mHsmThread;
+    private SmHandler mSmHandler;
+    private HandlerThread mSmThread;
 
     /**
      * Initialize.
@@ -981,28 +1154,28 @@ public class HierarchicalStateMachine {
      */
     private void initStateMachine(String name, Looper looper) {
         mName = name;
-        mHsmHandler = new HsmHandler(looper, this);
+        mSmHandler = new SmHandler(looper, this);
     }
 
     /**
-     * Constructor creates an HSM with its own thread.
+     * Constructor creates a StateMachine with its own thread.
      *
      * @param name of the state machine
      */
-    protected HierarchicalStateMachine(String name) {
-        mHsmThread = new HandlerThread(name);
-        mHsmThread.start();
-        Looper looper = mHsmThread.getLooper();
+    protected StateMachine(String name) {
+        mSmThread = new HandlerThread(name);
+        mSmThread.start();
+        Looper looper = mSmThread.getLooper();
 
         initStateMachine(name, looper);
     }
 
     /**
-     * Constructor creates an HSMStateMachine using the looper.
+     * Constructor creates an StateMachine using the looper.
      *
      * @param name of the state machine
      */
-    protected HierarchicalStateMachine(String name, Looper looper) {
+    protected StateMachine(String name, Looper looper) {
         initStateMachine(name, looper);
     }
 
@@ -1011,30 +1184,30 @@ public class HierarchicalStateMachine {
      * @param state the state to add
      * @param parent the parent of state
      */
-    protected final void addState(HierarchicalState state, HierarchicalState parent) {
-        mHsmHandler.addState(state, parent);
+    protected final void addState(State state, State parent) {
+        mSmHandler.addState(state, parent);
     }
 
     /**
      * @return current message
      */
     protected final Message getCurrentMessage() {
-        return mHsmHandler.getCurrentMessage();
+        return mSmHandler.getCurrentMessage();
     }
 
     /**
      * @return current state
      */
-    protected final HierarchicalState getCurrentState() {
-        return mHsmHandler.getCurrentState();
+    protected final IState getCurrentState() {
+        return mSmHandler.getCurrentState();
     }
 
     /**
      * Add a new state to the state machine, parent will be null
      * @param state to add
      */
-    protected final void addState(HierarchicalState state) {
-        mHsmHandler.addState(state, null);
+    protected final void addState(State state) {
+        mSmHandler.addState(state, null);
     }
 
     /**
@@ -1043,8 +1216,8 @@ public class HierarchicalStateMachine {
      *
      * @param initialState is the state which will receive the first message.
      */
-    protected final void setInitialState(HierarchicalState initialState) {
-        mHsmHandler.setInitialState(initialState);
+    protected final void setInitialState(State initialState) {
+        mSmHandler.setInitialState(initialState);
     }
 
     /**
@@ -1055,8 +1228,8 @@ public class HierarchicalStateMachine {
      *
      * @param destState will be the state that receives the next message.
      */
-    protected final void transitionTo(HierarchicalState destState) {
-        mHsmHandler.transitionTo(destState);
+    protected final void transitionTo(IState destState) {
+        mSmHandler.transitionTo(destState);
     }
 
     /**
@@ -1067,7 +1240,7 @@ public class HierarchicalStateMachine {
      * will be called.
      */
     protected final void transitionToHaltingState() {
-        mHsmHandler.transitionTo(mHsmHandler.mHaltingState);
+        mSmHandler.transitionTo(mSmHandler.mHaltingState);
     }
 
     /**
@@ -1080,7 +1253,7 @@ public class HierarchicalStateMachine {
      * @param msg is deferred until the next transition.
      */
     protected final void deferMessage(Message msg) {
-        mHsmHandler.deferMessage(msg);
+        mSmHandler.deferMessage(msg);
     }
 
 
@@ -1090,9 +1263,7 @@ public class HierarchicalStateMachine {
      * @param msg that couldn't be handled.
      */
     protected void unhandledMessage(Message msg) {
-        if (false) {
-            Log.e(TAG, mName + " - unhandledMessage: msg.what=" + msg.what);
-        }
+        if (mSmHandler.mDbg) Log.e(TAG, mName + " - unhandledMessage: msg.what=" + msg.what);
     }
 
     /**
@@ -1103,16 +1274,18 @@ public class HierarchicalStateMachine {
     }
 
     /**
-     * Called after the message that called transitionToHalting
-     * is called and should be overridden by StateMachine's that
-     * call transitionToHalting.
+     * This will be called once after handling a message that called
+     * transitionToHalting. All subsequent messages will invoke
+     * {@link StateMachine#haltedProcessMessage(Message)}
      */
     protected void halting() {
     }
 
     /**
-     * Called after the quitting message was NOT handled and
-     * just before the quit actually occurs.
+     * This will be called once after a quit message that was NOT handled by
+     * the derived StateMachine. The StateMachine will stop and any subsequent messages will be
+     * ignored. In addition, if this StateMachine created the thread, the thread will
+     * be stopped after this method returns.
      */
     protected void quitting() {
     }
@@ -1130,35 +1303,35 @@ public class HierarchicalStateMachine {
      * @param maxSize number of messages to maintain at anyone time.
      */
     public final void setProcessedMessagesSize(int maxSize) {
-        mHsmHandler.setProcessedMessagesSize(maxSize);
+        mSmHandler.setProcessedMessagesSize(maxSize);
     }
 
     /**
      * @return number of messages processed
      */
     public final int getProcessedMessagesSize() {
-        return mHsmHandler.getProcessedMessagesSize();
+        return mSmHandler.getProcessedMessagesSize();
     }
 
     /**
      * @return the total number of messages processed
      */
     public final int getProcessedMessagesCount() {
-        return mHsmHandler.getProcessedMessagesCount();
+        return mSmHandler.getProcessedMessagesCount();
     }
 
     /**
-     * @return a processed message
+     * @return a processed message information
      */
-    public final ProcessedMessages.Info getProcessedMessage(int index) {
-        return mHsmHandler.getProcessedMessage(index);
+    public final ProcessedMessageInfo getProcessedMessageInfo(int index) {
+        return mSmHandler.getProcessedMessageInfo(index);
     }
 
     /**
      * @return Handler
      */
     public final Handler getHandler() {
-        return mHsmHandler;
+        return mSmHandler;
     }
 
     /**
@@ -1168,7 +1341,7 @@ public class HierarchicalStateMachine {
      */
     public final Message obtainMessage()
     {
-        return Message.obtain(mHsmHandler);
+        return Message.obtain(mSmHandler);
     }
 
     /**
@@ -1178,7 +1351,7 @@ public class HierarchicalStateMachine {
      * @return message
      */
     public final Message obtainMessage(int what) {
-        return Message.obtain(mHsmHandler, what);
+        return Message.obtain(mSmHandler, what);
     }
 
     /**
@@ -1191,7 +1364,7 @@ public class HierarchicalStateMachine {
      */
     public final Message obtainMessage(int what, Object obj)
     {
-        return Message.obtain(mHsmHandler, what, obj);
+        return Message.obtain(mSmHandler, what, obj);
     }
 
     /**
@@ -1205,7 +1378,7 @@ public class HierarchicalStateMachine {
      */
     public final Message obtainMessage(int what, int arg1, int arg2)
     {
-        return Message.obtain(mHsmHandler, what, arg1, arg2);
+        return Message.obtain(mSmHandler, what, arg1, arg2);
     }
 
     /**
@@ -1220,107 +1393,107 @@ public class HierarchicalStateMachine {
      */
     public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
     {
-        return Message.obtain(mHsmHandler, what, arg1, arg2, obj);
+        return Message.obtain(mSmHandler, what, arg1, arg2, obj);
     }
 
     /**
      * Enqueue a message to this state machine.
      */
     public final void sendMessage(int what) {
-        mHsmHandler.sendMessage(obtainMessage(what));
+        mSmHandler.sendMessage(obtainMessage(what));
     }
 
     /**
      * Enqueue a message to this state machine.
      */
     public final void sendMessage(int what, Object obj) {
-        mHsmHandler.sendMessage(obtainMessage(what,obj));
+        mSmHandler.sendMessage(obtainMessage(what,obj));
     }
 
     /**
      * Enqueue a message to this state machine.
      */
     public final void sendMessage(Message msg) {
-        mHsmHandler.sendMessage(msg);
+        mSmHandler.sendMessage(msg);
     }
 
     /**
      * Enqueue a message to this state machine after a delay.
      */
     public final void sendMessageDelayed(int what, long delayMillis) {
-        mHsmHandler.sendMessageDelayed(obtainMessage(what), delayMillis);
+        mSmHandler.sendMessageDelayed(obtainMessage(what), delayMillis);
     }
 
     /**
      * Enqueue a message to this state machine after a delay.
      */
     public final void sendMessageDelayed(int what, Object obj, long delayMillis) {
-        mHsmHandler.sendMessageDelayed(obtainMessage(what, obj), delayMillis);
+        mSmHandler.sendMessageDelayed(obtainMessage(what, obj), delayMillis);
     }
 
     /**
      * Enqueue a message to this state machine after a delay.
      */
     public final void sendMessageDelayed(Message msg, long delayMillis) {
-        mHsmHandler.sendMessageDelayed(msg, delayMillis);
+        mSmHandler.sendMessageDelayed(msg, delayMillis);
     }
 
     /**
      * Enqueue a message to the front of the queue for this state machine.
-     * Protected, may only be called by instances of HierarchicalStateMachine.
+     * Protected, may only be called by instances of StateMachine.
      */
     protected final void sendMessageAtFrontOfQueue(int what, Object obj) {
-        mHsmHandler.sendMessageAtFrontOfQueue(obtainMessage(what, obj));
+        mSmHandler.sendMessageAtFrontOfQueue(obtainMessage(what, obj));
     }
 
     /**
      * Enqueue a message to the front of the queue for this state machine.
-     * Protected, may only be called by instances of HierarchicalStateMachine.
+     * Protected, may only be called by instances of StateMachine.
      */
     protected final void sendMessageAtFrontOfQueue(int what) {
-        mHsmHandler.sendMessageAtFrontOfQueue(obtainMessage(what));
+        mSmHandler.sendMessageAtFrontOfQueue(obtainMessage(what));
     }
 
     /**
      * Enqueue a message to the front of the queue for this state machine.
-     * Protected, may only be called by instances of HierarchicalStateMachine.
+     * Protected, may only be called by instances of StateMachine.
      */
     protected final void sendMessageAtFrontOfQueue(Message msg) {
-        mHsmHandler.sendMessageAtFrontOfQueue(msg);
+        mSmHandler.sendMessageAtFrontOfQueue(msg);
     }
 
     /**
      * Removes a message from the message queue.
-     * Protected, may only be called by instances of HierarchicalStateMachine.
+     * Protected, may only be called by instances of StateMachine.
      */
     protected final void removeMessages(int what) {
-        mHsmHandler.removeMessages(what);
+        mSmHandler.removeMessages(what);
     }
 
     /**
      * Conditionally quit the looper and stop execution.
      *
-     * This sends the HSM_QUIT_MSG to the state machine and
+     * This sends the SM_QUIT_MSG to the state machine and
      * if not handled by any state's processMessage then the
      * state machine will be stopped and no further messages
      * will be processed.
      */
     public final void quit() {
-        mHsmHandler.quit();
+        mSmHandler.quit();
     }
 
     /**
      * @return ture if msg is quit
      */
     protected final boolean isQuit(Message msg) {
-        return mHsmHandler.isQuit(msg);
+        return mSmHandler.isQuit(msg);
     }
 
     /**
      * @return if debugging is enabled
      */
     public boolean isDbg() {
-        return mHsmHandler.isDbg();
+        return mSmHandler.isDbg();
     }
 
     /**
@@ -1329,7 +1502,7 @@ public class HierarchicalStateMachine {
      * @param dbg is true to enable debugging.
      */
     public void setDbg(boolean dbg) {
-        mHsmHandler.setDbg(dbg);
+        mSmHandler.setDbg(dbg);
     }
 
     /**
@@ -1337,6 +1510,6 @@ public class HierarchicalStateMachine {
      */
     public void start() {
         /** Send the complete construction message */
-        mHsmHandler.completeConstruction();
+        mSmHandler.completeConstruction();
     }
 }
index 0dc0422..c3f6329 100644 (file)
@@ -33,7 +33,9 @@ import android.provider.Settings;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.View;
 import android.widget.Button;
+import android.widget.TextView;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -775,6 +777,16 @@ public class LockPatternUtils {
         setBoolean(LOCKOUT_PERMANENT_KEY, locked);
     }
 
+    public boolean isEmergencyCallCapable() {
+        return mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_voice_capable);
+    }
+
+    public boolean isPukUnlockScreenEnable() {
+        return mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_enable_puk_unlock_screen);
+    }
+
     /**
      * @return A formatted string of the next alarm (for showing on the lock screen),
      *   or null if there is no next alarm.
@@ -827,11 +839,22 @@ public class LockPatternUtils {
     }
 
     /**
-     * Sets the text on the emergency button to indicate what action will be taken.
+     * Sets the emergency button visibility based on isEmergencyCallCapable().
+     *
+     * If the emergency button is visible, sets the text on the emergency button
+     * to indicate what action will be taken.
+     *
      * If there's currently a call in progress, the button will take them to the call
      * @param button the button to update
      */
     public void updateEmergencyCallButtonState(Button button) {
+        if (isEmergencyCallCapable()) {
+            button.setVisibility(View.VISIBLE);
+        } else {
+            button.setVisibility(View.GONE);
+            return;
+        }
+
         int newState = TelephonyManager.getDefault().getCallState();
         int textId;
         if (newState == TelephonyManager.CALL_STATE_OFFHOOK) {
@@ -848,6 +871,18 @@ public class LockPatternUtils {
     }
 
     /**
+     * Sets the visibility of emergency call prompt based on emergency capable
+     * @param emergencyText the emergency call text to be updated
+     */
+    public void updateEmergencyCallText(TextView emergencyText) {
+        if (isEmergencyCallCapable()) {
+            emergencyText.setVisibility(View.VISIBLE);
+        } else {
+            emergencyText.setVisibility(View.GONE);
+        }
+    }
+
+    /**
      * Resumes a call in progress. Typically launched from the EmergencyCall button
      * on various lockscreens.
      *
old mode 100644 (file)
new mode 100755 (executable)
index 8edfe52..3f185aa
@@ -29,6 +29,8 @@ import java.io.UnsupportedEncodingException;
 import java.util.Arrays;
 import java.util.HashMap;
 
+import android.content.res.Resources;
+
 public class PduParser {
     /**
      *  The next are WAP values defined in WSP specification.
@@ -1557,43 +1559,55 @@ public class PduParser {
                          * Attachment = <Octet 129>
                          * Inline = <Octet 130>
                          */
-                        int len = parseValueLength(pduDataStream);
-                        pduDataStream.mark(1);
-                        int thisStartPos = pduDataStream.available();
-                        int thisEndPos = 0;
-                        int value = pduDataStream.read();
-
-                        if (value == PduPart.P_DISPOSITION_FROM_DATA ) {
-                            part.setContentDisposition(PduPart.DISPOSITION_FROM_DATA);
-                        } else if (value == PduPart.P_DISPOSITION_ATTACHMENT) {
-                            part.setContentDisposition(PduPart.DISPOSITION_ATTACHMENT);
-                        } else if (value == PduPart.P_DISPOSITION_INLINE) {
-                            part.setContentDisposition(PduPart.DISPOSITION_INLINE);
-                        } else {
-                            pduDataStream.reset();
-                            /* Token-text */
-                            part.setContentDisposition(parseWapString(pduDataStream, TYPE_TEXT_STRING));
-                        }
 
-                        /* get filename parameter and skip other parameters */
-                        thisEndPos = pduDataStream.available();
-                        if (thisStartPos - thisEndPos < len) {
-                            value = pduDataStream.read();
-                            if (value == PduPart.P_FILENAME) { //filename is text-string
-                                part.setFilename(parseWapString(pduDataStream, TYPE_TEXT_STRING));
+                        /*
+                         * some carrier mmsc servers do not support content_disposition
+                         * field correctly
+                         */
+                        boolean contentDisposition = Resources.getSystem().getBoolean(com
+                                .android.internal.R.bool.config_mms_content_disposition_support);
+
+                        if (contentDisposition) {
+                            int len = parseValueLength(pduDataStream);
+                            pduDataStream.mark(1);
+                            int thisStartPos = pduDataStream.available();
+                            int thisEndPos = 0;
+                            int value = pduDataStream.read();
+
+                            if (value == PduPart.P_DISPOSITION_FROM_DATA ) {
+                                part.setContentDisposition(PduPart.DISPOSITION_FROM_DATA);
+                            } else if (value == PduPart.P_DISPOSITION_ATTACHMENT) {
+                                part.setContentDisposition(PduPart.DISPOSITION_ATTACHMENT);
+                            } else if (value == PduPart.P_DISPOSITION_INLINE) {
+                                part.setContentDisposition(PduPart.DISPOSITION_INLINE);
+                            } else {
+                                pduDataStream.reset();
+                                /* Token-text */
+                                part.setContentDisposition(parseWapString(pduDataStream
+                                        , TYPE_TEXT_STRING));
                             }
 
-                            /* skip other parameters */
+                            /* get filename parameter and skip other parameters */
                             thisEndPos = pduDataStream.available();
                             if (thisStartPos - thisEndPos < len) {
-                                int last = len - (thisStartPos - thisEndPos);
-                                byte[] temp = new byte[last];
-                                pduDataStream.read(temp, 0, last);
+                                value = pduDataStream.read();
+                                if (value == PduPart.P_FILENAME) { //filename is text-string
+                                    part.setFilename(parseWapString(pduDataStream
+                                            , TYPE_TEXT_STRING));
+                                }
+
+                                /* skip other parameters */
+                                thisEndPos = pduDataStream.available();
+                                if (thisStartPos - thisEndPos < len) {
+                                    int last = len - (thisStartPos - thisEndPos);
+                                    byte[] temp = new byte[last];
+                                    pduDataStream.read(temp, 0, last);
+                                }
                             }
-                        }
 
-                        tempPos = pduDataStream.available();
-                        lastLen = length - (startPos - tempPos);
+                            tempPos = pduDataStream.available();
+                            lastLen = length - (startPos - tempPos);
+                        }
                         break;
                     default:
                         if (LOCAL_LOGV) {
index 7c3fad7..866ca1e 100644 (file)
@@ -235,7 +235,7 @@ public final class PduCache extends AbstractCache<Uri, PduCacheEntry> {
     }
 
     private void removeFromMessageBoxes(Uri key, PduCacheEntry entry) {
-        HashSet<Uri> msgBox = mThreads.get(entry.getMessageBox());
+        HashSet<Uri> msgBox = mThreads.get(Long.valueOf(entry.getMessageBox()));
         if (msgBox != null) {
             msgBox.remove(key);
         }
index 4becad7..068fe67 100644 (file)
 extern "C" {
 int ifc_enable(const char *ifname);
 int ifc_disable(const char *ifname);
-int ifc_add_route(const char *ifname, const char *destStr, uint32_t prefixLen, const char *gwStr);
-int ifc_remove_host_routes(const char *ifname);
-int ifc_get_default_route(const char *ifname);
-int ifc_remove_default_route(const char *ifname);
-int ifc_reset_connections(const char *ifname);
+int ifc_reset_connections(const char *ifname, int reset_mask);
 
 int dhcp_do_request(const char *ifname,
                     const char *ipaddr,
@@ -68,7 +64,6 @@ static struct fieldIds {
     jclass dhcpInfoInternalClass;
     jmethodID constructorId;
     jfieldID ipaddress;
-    jfieldID gateway;
     jfieldID prefixLength;
     jfieldID dns1;
     jfieldID dns2;
@@ -96,62 +91,17 @@ static jint android_net_utils_disableInterface(JNIEnv* env, jobject clazz, jstri
     return (jint)result;
 }
 
-static jint android_net_utils_addRoute(JNIEnv* env, jobject clazz, jstring ifname,
-          jstring dst, jint prefixLength, jstring gw)
+static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz,
+      jstring ifname, jint mask)
 {
     int result;
 
     const char *nameStr = env->GetStringUTFChars(ifname, NULL);
-    const char *dstStr = env->GetStringUTFChars(dst, NULL);
-    const char *gwStr = NULL;
-    if (gw != NULL) {
-        gwStr = env->GetStringUTFChars(gw, NULL);
-    }
-    result = ::ifc_add_route(nameStr, dstStr, prefixLength, gwStr);
-    env->ReleaseStringUTFChars(ifname, nameStr);
-    env->ReleaseStringUTFChars(dst, dstStr);
-    if (gw != NULL) {
-        env->ReleaseStringUTFChars(gw, gwStr);
-    }
-    return (jint)result;
-}
-
-static jint android_net_utils_removeHostRoutes(JNIEnv* env, jobject clazz, jstring ifname)
-{
-    int result;
-
-    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
-    result = ::ifc_remove_host_routes(nameStr);
-    env->ReleaseStringUTFChars(ifname, nameStr);
-    return (jint)result;
-}
-
-static jint android_net_utils_getDefaultRoute(JNIEnv* env, jobject clazz, jstring ifname)
-{
-    int result;
-
-    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
-    result = ::ifc_get_default_route(nameStr);
-    env->ReleaseStringUTFChars(ifname, nameStr);
-    return (jint)result;
-}
 
-static jint android_net_utils_removeDefaultRoute(JNIEnv* env, jobject clazz, jstring ifname)
-{
-    int result;
+    LOGD("android_net_utils_resetConnections in env=%p clazz=%p iface=%s mask=0x%x\n",
+          env, clazz, nameStr, mask);
 
-    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
-    result = ::ifc_remove_default_route(nameStr);
-    env->ReleaseStringUTFChars(ifname, nameStr);
-    return (jint)result;
-}
-
-static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz, jstring ifname)
-{
-    int result;
-
-    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
-    result = ::ifc_reset_connections(nameStr);
+    result = ::ifc_reset_connections(nameStr, mask);
     env->ReleaseStringUTFChars(ifname, nameStr);
     return (jint)result;
 }
@@ -182,7 +132,30 @@ static jboolean android_net_utils_runDhcpCommon(JNIEnv* env, jobject clazz, jstr
     env->ReleaseStringUTFChars(ifname, nameStr);
     if (result == 0 && dhcpInfoInternalFieldIds.dhcpInfoInternalClass != NULL) {
         env->SetObjectField(info, dhcpInfoInternalFieldIds.ipaddress, env->NewStringUTF(ipaddr));
-        env->SetObjectField(info, dhcpInfoInternalFieldIds.gateway, env->NewStringUTF(gateway));
+
+        // set the gateway
+        jclass cls = env->FindClass("java/net/InetAddress");
+        jmethodID method = env->GetStaticMethodID(cls, "getByName",
+                "(Ljava/lang/String;)Ljava/net/InetAddress;");
+        jvalue args[1];
+        args[0].l = env->NewStringUTF(gateway);
+        jobject inetAddressObject = env->CallStaticObjectMethodA(cls, method, args);
+
+        if (!env->ExceptionOccurred()) {
+            cls = env->FindClass("android/net/RouteInfo");
+            method = env->GetMethodID(cls, "<init>", "(Ljava/net/InetAddress;)V");
+            args[0].l = inetAddressObject;
+            jobject routeInfoObject = env->NewObjectA(cls, method, args);
+
+            cls = env->FindClass("android/net/DhcpInfoInternal");
+            method = env->GetMethodID(cls, "addRoute", "(Landroid/net/RouteInfo;)V");
+            args[0].l = routeInfoObject;
+            env->CallVoidMethodA(info, method, args);
+        } else {
+            // if we have an exception (host not found perhaps), just don't add the route
+            env->ExceptionClear();
+        }
+
         env->SetIntField(info, dhcpInfoInternalFieldIds.prefixLength, prefixLength);
         env->SetObjectField(info, dhcpInfoInternalFieldIds.dns1, env->NewStringUTF(dns1));
         env->SetObjectField(info, dhcpInfoInternalFieldIds.dns2, env->NewStringUTF(dns2));
@@ -239,13 +212,7 @@ static JNINativeMethod gNetworkUtilMethods[] = {
 
     { "enableInterface", "(Ljava/lang/String;)I",  (void *)android_net_utils_enableInterface },
     { "disableInterface", "(Ljava/lang/String;)I",  (void *)android_net_utils_disableInterface },
-    { "addRoute", "(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;)I",
-       (void *)android_net_utils_addRoute },
-    { "removeHostRoutes", "(Ljava/lang/String;)I",  (void *)android_net_utils_removeHostRoutes },
-    { "getDefaultRouteNative", "(Ljava/lang/String;)I",
-       (void *)android_net_utils_getDefaultRoute },
-    { "removeDefaultRoute", "(Ljava/lang/String;)I",  (void *)android_net_utils_removeDefaultRoute },
-    { "resetConnections", "(Ljava/lang/String;)I",  (void *)android_net_utils_resetConnections },
+    { "resetConnections", "(Ljava/lang/String;I)I",  (void *)android_net_utils_resetConnections },
     { "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfoInternal;)Z",  (void *)android_net_utils_runDhcp },
     { "runDhcpRenew", "(Ljava/lang/String;Landroid/net/DhcpInfoInternal;)Z",  (void *)android_net_utils_runDhcpRenew },
     { "stopDhcp", "(Ljava/lang/String;)Z",  (void *)android_net_utils_stopDhcp },
@@ -262,7 +229,6 @@ int register_android_net_NetworkUtils(JNIEnv* env)
     if (dhcpInfoInternalFieldIds.dhcpInfoInternalClass != NULL) {
         dhcpInfoInternalFieldIds.constructorId = env->GetMethodID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "<init>", "()V");
         dhcpInfoInternalFieldIds.ipaddress = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "ipAddress", "Ljava/lang/String;");
-        dhcpInfoInternalFieldIds.gateway = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "gateway", "Ljava/lang/String;");
         dhcpInfoInternalFieldIds.prefixLength = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "prefixLength", "I");
         dhcpInfoInternalFieldIds.dns1 = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "dns1", "Ljava/lang/String;");
         dhcpInfoInternalFieldIds.dns2 = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "dns2", "Ljava/lang/String;");
index df272a7..eb1c437 100644 (file)
@@ -680,6 +680,15 @@ static jlong android_os_Binder_clearCallingIdentity(JNIEnv* env, jobject clazz)
 
 static void android_os_Binder_restoreCallingIdentity(JNIEnv* env, jobject clazz, jlong token)
 {
+    // XXX temporary sanity check to debug crashes.
+    int uid = (int)(token>>32);
+    if (uid > 0 && uid < 999) {
+        // In Android currently there are no uids in this range.
+        char buf[128];
+        sprintf(buf, "Restoring bad calling ident: 0x%Lx", token);
+        jniThrowException(env, "java/lang/IllegalStateException", buf);
+        return;
+    }
     IPCThreadState::self()->restoreCallingIdentity(token);
 }
 
index 441a3bf..4d40b57 100644 (file)
     <protected-broadcast android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
 
     <protected-broadcast android:name="android.nfc.action.LLCP_LINK_STATE_CHANGED" />
+    <protected-broadcast android:name="com.android.nfc_extras.action.RF_FIELD_ON_DETECTED" />
+    <protected-broadcast android:name="com.android.nfc_extras.action.RF_FIELD_OFF_DETECTED" />
+    <protected-broadcast android:name="com.android.nfc_extras.action.AID_SELECTED" />
+
     <protected-broadcast android:name="android.nfc.action.TRANSACTION_DETECTED" />
     <protected-broadcast android:name="android.intent.action.CLEAR_DNS_CACHE" />
     <protected-broadcast android:name="android.intent.action.PROXY_CHANGE" />
 
+
     <!-- ====================================== -->
     <!-- Permissions for things that cost money -->
     <!-- ====================================== -->
         android:label="@string/permlab_hardware_test"
         android:description="@string/permdesc_hardware_test" />
 
+    <!-- Allows access to configure network interfaces, configure/use IPSec, etc.
+         @hide -->
+    <permission android:name="android.permission.NET_ADMIN"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="signature" />
+
     <!-- =========================================== -->
     <!-- Permissions associated with telephony state -->
     <!-- =========================================== -->
diff --git a/core/res/res/layout/keyguard_screen_sim_puk_landscape.xml b/core/res/res/layout/keyguard_screen_sim_puk_landscape.xml
new file mode 100644 (file)
index 0000000..d58fb23
--- /dev/null
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/background_dark"
+        >
+
+    <LinearLayout android:id="@+id/topDisplayGroup"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <!-- header text ('Enter Puk Code') -->
+        <TextView android:id="@+id/headerText"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+        <!-- Carrier info -->
+        <TextView android:id="@+id/carrier"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="9dip"
+            android:gravity="center"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+        <LinearLayout
+            android:orientation="horizontal"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <LinearLayout
+                android:orientation="vertical"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_marginRight="10dip"
+                android:layout_marginLeft="10dip">
+                <TextView android:id="@+id/enter_puk"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:gravity="center_vertical"
+                    android:text="@android:string/keyguard_password_enter_puk_prompt"
+                    android:textSize="30sp"
+                    android:layout_marginBottom="10dip"/>
+                <TextView android:id="@+id/enter_pin"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:gravity="center_vertical"
+                    android:text="@android:string/keyguard_password_enter_pin_prompt"
+                    android:textSize="30sp"
+                    android:layout_marginTop="10dip"/>
+            </LinearLayout>
+
+            <LinearLayout
+                  android:orientation="vertical"
+                  android:layout_width="wrap_content"
+                  android:layout_weight="1"
+                  android:layout_height="match_parent"
+                  android:paddingRight="0dip"
+                  android:layout_marginRight="10dip"
+                  android:layout_marginLeft="10dip">
+
+                  <LinearLayout
+                      android:layout_width="match_parent"
+                      android:layout_height="wrap_content"
+                      android:orientation="horizontal"
+                      android:layout_marginRight="6dip"
+                      android:layout_marginLeft="6dip"
+                      android:gravity="center_vertical"
+                      android:background="@android:drawable/edit_text">
+
+                      <!-- displays dots as user enters puk -->
+                      <TextView android:id="@+id/pukDisplay"
+                          android:layout_width="0dip"
+                          android:layout_height="wrap_content"
+                          android:layout_weight="1"
+                          android:maxLines="1"
+                          android:textAppearance="?android:attr/textAppearanceLargeInverse"
+                          android:textStyle="bold"
+                          android:inputType="textPassword"
+                      />
+
+                      <ImageButton android:id="@+id/pukDel"
+                          android:src="@android:drawable/ic_input_delete"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:layout_marginRight="-3dip"
+                          android:layout_marginBottom="-3dip"
+                      />
+                  </LinearLayout>
+
+
+                  <LinearLayout
+                      android:layout_width="match_parent"
+                      android:layout_height="wrap_content"
+                      android:orientation="horizontal"
+                      android:layout_marginRight="6dip"
+                      android:layout_marginLeft="6dip"
+                      android:gravity="center_vertical"
+                      android:background="@android:drawable/edit_text">
+
+                      <!-- displays dots as user enters new pin -->
+                      <TextView android:id="@+id/pinDisplay"
+                          android:layout_width="0dip"
+                          android:layout_height="wrap_content"
+                          android:layout_weight="1"
+                          android:maxLines="1"
+                          android:textAppearance="?android:attr/textAppearanceLargeInverse"
+                          android:textStyle="bold"
+                          android:inputType="textPassword"
+                      />
+
+                      <ImageButton android:id="@+id/pinDel"
+                          android:src="@android:drawable/ic_input_delete"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:layout_marginRight="-3dip"
+                          android:layout_marginBottom="-3dip"
+                      />
+                  </LinearLayout>
+              </LinearLayout>
+        </LinearLayout>
+    </LinearLayout>
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_alignParentBottom="true"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="8dip"
+        android:layout_marginLeft="8dip"
+        android:layout_marginRight="8dip">
+
+        <Button android:id="@+id/ok"
+            android:text="@android:string/ok"
+            android:layout_alignParentBottom="true"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1.0"
+            android:layout_marginBottom="8dip"
+            android:layout_marginRight="8dip"
+            android:textSize="18sp"
+            />
+
+        <Button android:id="@+id/emergencyCall"
+            android:text="@android:string/lockscreen_emergency_call"
+            android:layout_alignParentBottom="true"
+            android:layout_centerHorizontal="true"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1.0"
+            android:layout_marginBottom="8dip"
+            android:layout_marginLeft="8dip"
+            android:textSize="18sp"
+            android:drawableLeft="@drawable/ic_emergency"
+            android:drawablePadding="8dip"
+        />
+    </LinearLayout>
+
+</RelativeLayout>
diff --git a/core/res/res/layout/keyguard_screen_sim_puk_portrait.xml b/core/res/res/layout/keyguard_screen_sim_puk_portrait.xml
new file mode 100644 (file)
index 0000000..5e392ef
--- /dev/null
@@ -0,0 +1,185 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:background="@android:color/background_dark"
+    android:gravity="center_horizontal">
+
+    <LinearLayout android:id="@+id/topDisplayGroup"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <!-- header text ('Enter Puk Code') -->
+        <TextView android:id="@+id/headerText"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+        <!-- Carrier info -->
+        <TextView android:id="@+id/carrier"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="9dip"
+            android:gravity="center"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+        <LinearLayout
+            android:orientation="horizontal"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <LinearLayout
+                android:orientation="vertical"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_marginRight="10dip"
+                android:layout_marginLeft="10dip">
+                <TextView android:id="@+id/enter_puk"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:gravity="center_vertical"
+                    android:text="@android:string/keyguard_password_enter_puk_prompt"
+                    android:textSize="30sp"
+                    android:layout_marginBottom="10dip"/>
+                <TextView android:id="@+id/enter_pin"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:gravity="center_vertical"
+                    android:text="@android:string/keyguard_password_enter_pin_prompt"
+                    android:textSize="30sp"
+                    android:layout_marginTop="10dip"/>
+            </LinearLayout>
+
+            <LinearLayout
+                  android:orientation="vertical"
+                  android:layout_width="wrap_content"
+                  android:layout_weight="1"
+                  android:layout_height="match_parent"
+                  android:paddingRight="0dip"
+                  android:layout_marginRight="10dip"
+                  android:layout_marginLeft="10dip">
+
+                  <LinearLayout
+                      android:layout_width="match_parent"
+                      android:layout_height="wrap_content"
+                      android:orientation="horizontal"
+                      android:layout_marginRight="6dip"
+                      android:layout_marginLeft="6dip"
+                      android:gravity="center_vertical"
+                      android:background="@android:drawable/edit_text">
+
+                      <!-- displays dots as user enters puk -->
+                      <TextView android:id="@+id/pukDisplay"
+                          android:layout_width="0dip"
+                          android:layout_height="wrap_content"
+                          android:layout_weight="1"
+                          android:maxLines="1"
+                          android:textAppearance="?android:attr/textAppearanceLargeInverse"
+                          android:textStyle="bold"
+                          android:inputType="textPassword"
+                      />
+
+                      <ImageButton android:id="@+id/pukDel"
+                          android:src="@android:drawable/ic_input_delete"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:layout_marginRight="-3dip"
+                          android:layout_marginBottom="-3dip"
+                      />
+                  </LinearLayout>
+
+
+                  <LinearLayout
+                      android:layout_width="match_parent"
+                      android:layout_height="wrap_content"
+                      android:orientation="horizontal"
+                      android:layout_marginRight="6dip"
+                      android:layout_marginLeft="6dip"
+                      android:gravity="center_vertical"
+                      android:background="@android:drawable/edit_text">
+
+                      <!-- displays dots as user enters new pin -->
+                      <TextView android:id="@+id/pinDisplay"
+                          android:layout_width="0dip"
+                          android:layout_height="wrap_content"
+                          android:layout_weight="1"
+                          android:maxLines="1"
+                          android:textAppearance="?android:attr/textAppearanceLargeInverse"
+                          android:textStyle="bold"
+                          android:inputType="textPassword"
+                      />
+
+                      <ImageButton android:id="@+id/pinDel"
+                          android:src="@android:drawable/ic_input_delete"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:layout_marginRight="-3dip"
+                          android:layout_marginBottom="-3dip"
+                      />
+                  </LinearLayout>
+              </LinearLayout>
+        </LinearLayout>
+    </LinearLayout>
+
+    <include
+        android:id="@+id/keyPad"
+        layout="@android:layout/twelve_key_entry"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/topDisplayGroup"
+        android:layout_marginTop="10dip"
+    />
+
+    <!-- spacer below keypad -->
+    <View
+        android:id="@+id/spacerBottom"
+        android:layout_width="match_parent"
+        android:layout_height="1dip"
+        android:layout_marginTop="6dip"
+        android:layout_above="@id/emergencyCall"
+        android:background="@android:drawable/divider_horizontal_dark"
+    />
+
+    <!-- The emergency button should take the rest of the space and be centered vertically -->
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1"
+        android:gravity="center"
+        android:orientation="vertical">
+
+        <!-- emergency call button -->
+        <Button
+            android:id="@+id/emergencyCall"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:drawableLeft="@android:drawable/ic_emergency"
+            android:drawablePadding="8dip"
+            android:text="@android:string/lockscreen_emergency_call"
+        />
+    </LinearLayout>
+
+</LinearLayout>
old mode 100644 (file)
new mode 100755 (executable)
index 5c8b9a1..f49be42
          manager will disable alpha trasformation in animations where not
          strictly needed. -->
     <bool name="config_sf_limitedAlpha">false</bool>
+
+    <!-- Default value used to block data calls if ims is not
+         connected.  If you use the ims apn DCT will block
+         any other apn from connecting until ims apn is connected-->
+    <bool name="ImsConnectedDefaultValue">false</bool>
     
     <!-- Flag indicating whether the surface flinger is inefficient
          at performing a blur.  Used by parts of the UI to turn off
     <!-- This string array should be overridden by the device to present a list of network
          attributes.  This is used by the connectivity manager to decide which networks can coexist
          based on the hardware -->
-    <!-- An Array of "[Connection name],[ConnectivityManager connection type],
-         [associated radio-type],[priority]  -->
+    <!-- An Array of "[Connection name],[ConnectivityManager.TYPE_xxxx],
+         [associated radio-type],[priority],[restoral-timer(ms)],[dependencyMet]  -->
+    <!-- the 5th element "resore-time" indicates the number of milliseconds to delay
+         before automatically restore the default connection.  Set -1 if the connection
+         does not require auto-restore. -->
+    <!-- the 6th element indicates boot-time dependency-met value. -->
     <string-array translatable="false" name="networkAttributes">
-        <item>"wifi,1,1,1"</item>
-        <item>"mobile,0,0,0"</item>
-        <item>"mobile_mms,2,0,2"</item>
-        <item>"mobile_supl,3,0,2"</item>
-        <item>"mobile_hipri,5,0,3"</item>
+        <item>"wifi,1,1,1,-1,true"</item>
+        <item>"mobile,0,0,0,-1,true"</item>
+        <item>"mobile_mms,2,0,2,60000,true"</item>
+        <item>"mobile_supl,3,0,2,60000,true"</item>
+        <item>"mobile_hipri,5,0,3,60000,true"</item>
+        <item>"mobile_fota,10,0,2,60000,true"</item>
+        <item>"mobile_ims,11,0,2,60000,true"</item>
+        <item>"mobile_cbs,12,0,2,60000,true"</item>
     </string-array>
 
+    <!-- Array of ConnectivityManager.TYPE_xxxx constants for networks that may only
+         be controlled by systemOrSignature apps.  -->
+    <integer-array translatable="false" name="config_protectedNetworks">
+        <item>10</item>
+        <item>11</item>
+        <item>12</item>
+    </integer-array>
+
     <!-- This string array should be overridden by the device to present a list of radio
          attributes.  This is used by the connectivity manager to decide which networks can coexist
          based on the hardware -->
     <string-array translatable="false" name="config_tether_dhcp_range">
     </string-array>
 
-    <!-- Regex array of allowable upstream ifaces for tethering - for example if you want
-         tethering on a new interface called "foo2" add <item>"foo\\d"</item> to the array -->
-    <string-array translatable="false" name="config_tether_upstream_regexs">
-    </string-array>
-
-    <!-- Boolean indicating if we require the use of DUN on mobile for tethering.
-         Note that this defaults to false so that if you move to a carrier that
-         hasn't configured anything tethering will still work.  If you'd rather
-         make the device untetherable on unconfigured devices, set to true -->
-    <bool translatable="false" name="config_tether_dun_required">false</bool>
+    <!-- Array of ConnectivityManager.TYPE_xxxx values allowable for tethering -->
+    <!-- Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or
+    <!== [0,1,5,7] for TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI and TYPE_BLUETOOTH -->
+    <integer-array translatable="false" name="config_tether_upstream_types">
+        <item>1</item>
+        <item>4</item>
+    </integer-array>
 
     <!-- String containing the apn value for tethering.  May be overriden by secure settings
          TETHER_DUN_APN.  Value is a comma separated series of strings:
     <!-- Diable lockscreen rotation by default -->
     <bool name="config_enableLockScreenRotation">false</bool>
 
+    <!-- Diable puk unlockscreen by default.
+         If unlock screen is disabled, the puk should be unlocked through Emergency Dialer -->
+    <bool name="config_enable_puk_unlock_screen">false</bool>
+
     <!-- Control the behavior when the user long presses the power button.
             0 - Nothing
             1 - Recent apps dialog
 
     <!-- The VoiceMail default value is displayed to my own number if it is true -->
     <bool name="config_telephony_use_own_number_for_voicemail">false</bool>
+
+    <!-- If this value is true, Sms encoded as octet is decoded by utf8 decoder.
+         If false, decoded by Latin decoder. -->
+    <bool name="config_sms_utf8_support">false</bool>
+
+    <!-- If this value is true, The mms content-disposition field is supported correctly.
+         If false, Content-disposition fragments are ignored -->
+    <bool name="config_mms_content_disposition_support">true</bool>
+
+    <!-- If this value is true, the carrier supports sms delivery reports.
+         If false, sms delivery reports are not supported and the preference
+         option to enable/disable delivery reports is removed in the Messaging app. -->
+    <bool name="config_sms_delivery_reports_support">true</bool>
+
+    <!-- If this value is true, the carrier supports mms delivery reports.
+         If false, mms delivery reports are not supported and the preference
+         option to enable/disable delivery reports is removed in the Messaging app. -->
+    <bool name="config_mms_delivery_reports_support">true</bool>
+
+    <!-- If this value is true, the carrier supports mms read reports.
+         If false, mms read reports are not supported and the preference
+         option to enable/disable read reports is removed in the Messaging app. -->
+    <bool name="config_mms_read_reports_support">true</bool>
 </resources>
index b92bf88..48731b9 100755 (executable)
@@ -87,6 +87,8 @@
     <string name="mismatchPin">The PINs you entered do not match.</string>
     <!-- Displayed when a SIM PIN password is too long or too short. -->
     <string name="invalidPin">Type a PIN that is 4 to 8 numbers.</string>
+    <!-- Displayed when a SIM PUK password is too short. -->
+    <string name="invalidPuk">Type a PUK that is 8 numbers or longer.</string>
     <!-- Displayed to prompt the user to type the PUK password to unlock
          the SIM card. -->
     <string name="needPuk">Your SIM card is PUK-locked. Type the PUK code to unlock it.</string>
          Displayed in one line in a large font.  -->
     <string name="keyguard_password_enter_pin_code">Enter PIN code</string>
 
+    <!-- Instructions telling the user to enter their SIM PUK to unlock the keyguard.
+         Displayed in one line in a large font.  -->
+    <string name="keyguard_password_enter_puk_code">Enter PUK and new PIN code</string>
+
+    <!-- Prompt to enter SIM PUK in Edit Text Box in unlock screen -->
+    <string name="keyguard_password_enter_puk_prompt">PUK code</string>
+    <!-- Prompt to enter New SIM PIN in Edit Text Box in unlock screen -->
+    <string name="keyguard_password_enter_pin_prompt">New Pin Code</string>
+
     <!-- Displayed as hint in passwordEntry EditText on PasswordUnlockScreen [CHAR LIMIT=30]-->
     <string name="keyguard_password_entry_touch_hint"><font size="17">Touch to enter password</font></string>
 
     <string name="lockscreen_missing_sim_message" product="default">No SIM card in phone.</string>
     <!-- Shown in the lock screen to ask the user to insert a SIM card. -->
     <string name="lockscreen_missing_sim_instructions">Please insert a SIM card.</string>
+    <!-- Shown in the lock screen to ask the user to insert a SIM card when sim is missing or not readable. -->
+    <string name="lockscreen_missing_sim_instructions_long">The SIM card is missing or not readable. Please insert a SIM card.</string>
+    <!-- Shown in the lock screen to inform the user to SIM card is permanently disabled. -->
+    <string name="lockscreen_permanent_disabled_sim_instructions">Your SIM card is permanently disabled.\n
+    Please contact your wireless service provider to obtain another SIM card.</string>
 
     <!-- Shown in the lock screen when there is emergency calls only mode. -->
     <string name="emergency_calls_only" msgid="2485604591272668370">Emergency calls only</string>
     <!-- See SMS_DIALOG.  This is a button choice to disallow sending the SMSes.. -->
     <string name="sms_control_no">Cancel</string>
 
+    <!-- SIM swap and device reboot Dialog --> <skip />
+    <!-- See SIM_REMOVED_DIALOG.  This is the title of that dialog. -->
+    <string name="sim_removed_title">SIM card removed</string>
+    <!-- See SIM_REMOVED_DIALOG.  This is the message of that dialog. -->
+    <string name="sim_removed_message">The mobile network will be unavailable until you replace the SIM card.</string>
+    <!-- See SIM_REMOVED_DIALOG.  This is the button of that dialog. -->
+    <string name="sim_done_button">Done</string>
+    <!-- See SIM_ADDED_DIALOG.  This is the title of that dialog. -->
+    <string name="sim_added_title">SIM card added</string>
+    <!-- See SIM_ADDED_DIALOG.  This is the message of that dialog. -->
+    <string name="sim_added_message">You must restart your device to access the mobile network.</string>
+    <!-- See SIM_ADDED_DIALOG.  This is the button of that dialog. -->
+    <string name="sim_restart_button">Restart</string>
+
     <!-- Date/Time picker dialogs strings -->
 
     <!-- The title of the time picker dialog. [CHAR LIMIT=NONE] -->
index d22356d..27363e8 100644 (file)
@@ -30,6 +30,7 @@ import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.wifi.WifiConfiguration.ProxySettings;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
+import android.net.RouteInfo;
 import android.util.Log;
 
 import java.io.InputStream;
@@ -301,7 +302,7 @@ public class AccessPointParserHelper {
                     if (!InetAddress.isNumeric(gwAddr)) {
                         throw new SAXException();
                     }
-                    mLinkProperties.addGateway(InetAddress.getByName(gwAddr));
+                    mLinkProperties.addRoute(new RouteInfo(InetAddress.getByName(gwAddr)));
                 } catch (UnknownHostException e) {
                     throw new SAXException();
                 }
diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
new file mode 100644 (file)
index 0000000..e3b6b5f
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2010 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.net;
+
+import android.net.LinkProperties;
+import android.net.RouteInfo;
+import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
+import java.net.InetAddress;
+
+public class LinkPropertiesTest extends TestCase {
+    private static String ADDRV4 = "75.208.6.1";
+    private static String ADDRV6 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
+    private static String DNS1 = "75.208.7.1";
+    private static String DNS2 = "69.78.7.1";
+    private static String GATEWAY1 = "75.208.8.1";
+    private static String GATEWAY2 = "69.78.8.1";
+    private static String NAME = "qmi0";
+
+    @SmallTest
+    public void testEqualsNull() {
+        LinkProperties source = new LinkProperties();
+        LinkProperties target = new LinkProperties();
+
+        assertFalse(source == target);
+        assertTrue(source.equals(target));
+        assertTrue(source.hashCode() == target.hashCode());
+    }
+
+    @SmallTest
+    public void testEqualsSameOrder() {
+        try {
+            LinkProperties source = new LinkProperties();
+            source.setInterfaceName(NAME);
+            // set 2 link addresses
+            source.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            source.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            // set 2 dnses
+            source.addDns(NetworkUtils.numericToInetAddress(DNS1));
+            source.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            // set 2 gateways
+            source.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            source.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+
+            LinkProperties target = new LinkProperties();
+
+            // All fields are same
+            target.setInterfaceName(NAME);
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS1));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+
+            assertTrue(source.equals(target));
+            assertTrue(source.hashCode() == target.hashCode());
+
+            target.clear();
+            // change Interface Name
+            target.setInterfaceName("qmi1");
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS1));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+            assertFalse(source.equals(target));
+
+            target.clear();
+            target.setInterfaceName(NAME);
+            // change link addresses
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress("75.208.6.2"), 32));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS1));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+            assertFalse(source.equals(target));
+
+            target.clear();
+            target.setInterfaceName(NAME);
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            // change dnses
+            target.addDns(NetworkUtils.numericToInetAddress("75.208.7.2"));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+            assertFalse(source.equals(target));
+
+            target.clear();
+            target.setInterfaceName(NAME);
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS1));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            // change gateway
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress("75.208.8.2")));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+            assertFalse(source.equals(target));
+
+        } catch (Exception e) {
+            throw new RuntimeException(e.toString());
+            //fail();
+        }
+    }
+
+    @SmallTest
+    public void testEqualsDifferentOrder() {
+        try {
+            LinkProperties source = new LinkProperties();
+            source.setInterfaceName(NAME);
+            // set 2 link addresses
+            source.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            source.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            // set 2 dnses
+            source.addDns(NetworkUtils.numericToInetAddress(DNS1));
+            source.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            // set 2 gateways
+            source.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            source.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+
+            LinkProperties target = new LinkProperties();
+            // Exchange order
+            target.setInterfaceName(NAME);
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS2));
+            target.addDns(NetworkUtils.numericToInetAddress(DNS1));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+
+            assertTrue(source.equals(target));
+            assertTrue(source.hashCode() == target.hashCode());
+        } catch (Exception e) {
+            fail();
+        }
+    }
+
+    @SmallTest
+    public void testEqualsDuplicated() {
+        try {
+            LinkProperties source = new LinkProperties();
+            // set 3 link addresses, eg, [A, A, B]
+            source.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            source.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            source.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+
+            LinkProperties target = new LinkProperties();
+            // set 3 link addresses, eg, [A, B, B]
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV4), 32));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+            target.addLinkAddress(new LinkAddress(
+                    NetworkUtils.numericToInetAddress(ADDRV6), 128));
+
+            assertTrue(source.equals(target));
+            assertTrue(source.hashCode() == target.hashCode());
+        } catch (Exception e) {
+            fail();
+        }
+    }
+
+}
@@ -22,9 +22,9 @@ import android.os.Looper;
 import android.os.Message;
 import android.os.SystemClock;
 
-import com.android.internal.util.HierarchicalState;
-import com.android.internal.util.HierarchicalStateMachine;
-import com.android.internal.util.ProcessedMessages;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+import com.android.internal.util.StateMachine.ProcessedMessageInfo;
 
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -33,9 +33,9 @@ import android.util.Log;
 import junit.framework.TestCase;
 
 /**
- * Test for HierarchicalStateMachine.
+ * Test for StateMachine.
  */
-public class HierarchicalStateMachineTest extends TestCase {
+public class StateMachineTest extends TestCase {
     private static final int TEST_CMD_1 = 1;
     private static final int TEST_CMD_2 = 2;
     private static final int TEST_CMD_3 = 3;
@@ -45,12 +45,12 @@ public class HierarchicalStateMachineTest extends TestCase {
 
     private static final boolean DBG = true;
     private static final boolean WAIT_FOR_DEBUGGER = false;
-    private static final String TAG = "HierarchicalStateMachineTest";
+    private static final String TAG = "StateMachineTest";
 
     /**
      * Tests that we can quit the state machine.
      */
-    class StateMachineQuitTest extends HierarchicalStateMachine {
+    class StateMachineQuitTest extends StateMachine {
         private int mQuitCount = 0;
 
         StateMachineQuitTest(String name) {
@@ -65,8 +65,9 @@ public class HierarchicalStateMachineTest extends TestCase {
             setInitialState(mS1);
         }
 
-        class S1 extends HierarchicalState {
-            @Override protected boolean processMessage(Message message) {
+        class S1 extends State {
+            @Override
+            public boolean processMessage(Message message) {
                 if (isQuit(message)) {
                     mQuitCount += 1;
                     if (mQuitCount > 2) {
@@ -125,22 +126,22 @@ public class HierarchicalStateMachineTest extends TestCase {
 
         assertTrue(smQuitTest.getProcessedMessagesCount() == 9);
 
-        ProcessedMessages.Info pmi;
+        ProcessedMessageInfo pmi;
 
         // The first two message didn't quit and were handled by mS1
-        pmi = smQuitTest.getProcessedMessage(6);
-        assertEquals(HierarchicalStateMachine.HSM_QUIT_CMD, pmi.getWhat());
+        pmi = smQuitTest.getProcessedMessageInfo(6);
+        assertEquals(StateMachine.SM_QUIT_CMD, pmi.getWhat());
         assertEquals(smQuitTest.mS1, pmi.getState());
         assertEquals(smQuitTest.mS1, pmi.getOriginalState());
 
-        pmi = smQuitTest.getProcessedMessage(7);
-        assertEquals(HierarchicalStateMachine.HSM_QUIT_CMD, pmi.getWhat());
+        pmi = smQuitTest.getProcessedMessageInfo(7);
+        assertEquals(StateMachine.SM_QUIT_CMD, pmi.getWhat());
         assertEquals(smQuitTest.mS1, pmi.getState());
         assertEquals(smQuitTest.mS1, pmi.getOriginalState());
 
         // The last message was never handled so the states are null
-        pmi = smQuitTest.getProcessedMessage(8);
-        assertEquals(HierarchicalStateMachine.HSM_QUIT_CMD, pmi.getWhat());
+        pmi = smQuitTest.getProcessedMessageInfo(8);
+        assertEquals(StateMachine.SM_QUIT_CMD, pmi.getWhat());
         assertEquals(null, pmi.getState());
         assertEquals(null, pmi.getOriginalState());
 
@@ -150,7 +151,7 @@ public class HierarchicalStateMachineTest extends TestCase {
     /**
      * Test enter/exit can use transitionTo
      */
-    class StateMachineEnterExitTransitionToTest extends HierarchicalStateMachine {
+    class StateMachineEnterExitTransitionToTest extends StateMachine {
         StateMachineEnterExitTransitionToTest(String name) {
             super(name);
             mThisSm = this;
@@ -166,34 +167,38 @@ public class HierarchicalStateMachineTest extends TestCase {
             setInitialState(mS1);
         }
 
-        class S1 extends HierarchicalState {
-            @Override protected void enter() {
+        class S1 extends State {
+            @Override
+            public void enter() {
                 // Test that message is HSM_INIT_CMD
-                assertEquals(HSM_INIT_CMD, getCurrentMessage().what);
+                assertEquals(SM_INIT_CMD, getCurrentMessage().what);
 
                 // Test that a transition in enter and the initial state works
                 mS1EnterCount += 1;
                 transitionTo(mS2);
                 Log.d(TAG, "S1.enter");
             }
-            @Override protected void exit() {
+            @Override
+            public void exit() {
                 // Test that message is HSM_INIT_CMD
-                assertEquals(HSM_INIT_CMD, getCurrentMessage().what);
+                assertEquals(SM_INIT_CMD, getCurrentMessage().what);
 
                 mS1ExitCount += 1;
                 Log.d(TAG, "S1.exit");
             }
         }
 
-        class S2 extends HierarchicalState {
-            @Override protected void enter() {
+        class S2 extends State {
+            @Override
+            public void enter() {
                 // Test that message is HSM_INIT_CMD
-                assertEquals(HSM_INIT_CMD, getCurrentMessage().what);
+                assertEquals(SM_INIT_CMD, getCurrentMessage().what);
 
                 mS2EnterCount += 1;
                 Log.d(TAG, "S2.enter");
             }
-            @Override protected void exit() {
+            @Override
+            public void exit() {
                 // Test that message is TEST_CMD_1
                 assertEquals(TEST_CMD_1, getCurrentMessage().what);
 
@@ -202,7 +207,8 @@ public class HierarchicalStateMachineTest extends TestCase {
                 transitionTo(mS4);
                 Log.d(TAG, "S2.exit");
             }
-            @Override protected boolean processMessage(Message message) {
+            @Override
+            public boolean processMessage(Message message) {
                 // Start a transition to S3 but it will be
                 // changed to a transition to S4 in exit
                 transitionTo(mS3);
@@ -211,28 +217,32 @@ public class HierarchicalStateMachineTest extends TestCase {
             }
         }
 
-        class S3 extends HierarchicalState {
-            @Override protected void enter() {
+        class S3 extends State {
+            @Override
+            public void enter() {
                 // Test that we can do halting in an enter/exit
                 transitionToHaltingState();
                 mS3EnterCount += 1;
                 Log.d(TAG, "S3.enter");
             }
-            @Override protected void exit() {
+            @Override
+            public void exit() {
                 mS3ExitCount += 1;
                 Log.d(TAG, "S3.exit");
             }
         }
 
 
-        class S4 extends HierarchicalState {
-            @Override protected void enter() {
+        class S4 extends State {
+            @Override
+            public void enter() {
                 // Test that we can do halting in an enter/exit
                 transitionToHaltingState();
                 mS4EnterCount += 1;
                 Log.d(TAG, "S4.enter");
             }
-            @Override protected void exit() {
+            @Override
+            public void exit() {
                 mS4ExitCount += 1;
                 Log.d(TAG, "S4.exit");
             }
@@ -285,10 +295,10 @@ public class HierarchicalStateMachineTest extends TestCase {
 
         assertTrue(smEnterExitTranstionToTest.getProcessedMessagesCount() == 1);
 
-        ProcessedMessages.Info pmi;
+        ProcessedMessageInfo pmi;
 
         // Message should be handled by mS2.
-        pmi = smEnterExitTranstionToTest.getProcessedMessage(0);
+        pmi = smEnterExitTranstionToTest.getProcessedMessageInfo(0);
         assertEquals(TEST_CMD_1, pmi.getWhat());
         assertEquals(smEnterExitTranstionToTest.mS2, pmi.getState());
         assertEquals(smEnterExitTranstionToTest.mS2, pmi.getOriginalState());
@@ -310,7 +320,7 @@ public class HierarchicalStateMachineTest extends TestCase {
     /**
      * Tests that ProcessedMessage works as a circular buffer.
      */
-    class StateMachine0 extends HierarchicalStateMachine {
+    class StateMachine0 extends StateMachine {
         StateMachine0(String name) {
             super(name);
             mThisSm = this;
@@ -324,8 +334,9 @@ public class HierarchicalStateMachineTest extends TestCase {
             setInitialState(mS1);
         }
 
-        class S1 extends HierarchicalState {
-            @Override protected boolean processMessage(Message message) {
+        class S1 extends State {
+            @Override
+            public boolean processMessage(Message message) {
                 if (message.what == TEST_CMD_6) {
                     transitionToHaltingState();
                 }
@@ -369,18 +380,18 @@ public class HierarchicalStateMachineTest extends TestCase {
         assertTrue(sm0.getProcessedMessagesCount() == 6);
         assertTrue(sm0.getProcessedMessagesSize() == 3);
 
-        ProcessedMessages.Info pmi;
-        pmi = sm0.getProcessedMessage(0);
+        ProcessedMessageInfo pmi;
+        pmi = sm0.getProcessedMessageInfo(0);
         assertEquals(TEST_CMD_4, pmi.getWhat());
         assertEquals(sm0.mS1, pmi.getState());
         assertEquals(sm0.mS1, pmi.getOriginalState());
 
-        pmi = sm0.getProcessedMessage(1);
+        pmi = sm0.getProcessedMessageInfo(1);
         assertEquals(TEST_CMD_5, pmi.getWhat());
         assertEquals(sm0.mS1, pmi.getState());
         assertEquals(sm0.mS1, pmi.getOriginalState());
 
-        pmi = sm0.getProcessedMessage(2);
+        pmi = sm0.getProcessedMessageInfo(2);
         assertEquals(TEST_CMD_6, pmi.getWhat());
         assertEquals(sm0.mS1, pmi.getState());
         assertEquals(sm0.mS1, pmi.getOriginalState());
@@ -394,7 +405,7 @@ public class HierarchicalStateMachineTest extends TestCase {
      * in state mS1. With the first message it transitions to
      * itself which causes it to be exited and reentered.
      */
-    class StateMachine1 extends HierarchicalStateMachine {
+    class StateMachine1 extends StateMachine {
         StateMachine1(String name) {
             super(name);
             mThisSm = this;
@@ -408,12 +419,17 @@ public class HierarchicalStateMachineTest extends TestCase {
             if (DBG) Log.d(TAG, "StateMachine1: ctor X");
         }
 
-        class S1 extends HierarchicalState {
-            @Override protected void enter() {
+        class S1 extends State {
+            @Override
+            public void enter() {
                 mEnterCount++;
             }
-
-            @Override protected boolean processMessage(Message message) {
+            @Override
+            public void exit() {
+                mExitCount++;
+            }
+            @Override
+            public boolean processMessage(Message message) {
                 if (message.what == TEST_CMD_1) {
                     assertEquals(1, mEnterCount);
                     assertEquals(0, mExitCount);
@@ -425,10 +441,6 @@ public class HierarchicalStateMachineTest extends TestCase {
                 }
                 return HANDLED;
             }
-
-            @Override protected void exit() {
-                mExitCount++;
-            }
         }
 
         @Override
@@ -469,13 +481,13 @@ public class HierarchicalStateMachineTest extends TestCase {
 
         assertTrue(sm1.getProcessedMessagesSize() == 2);
 
-        ProcessedMessages.Info pmi;
-        pmi = sm1.getProcessedMessage(0);
+        ProcessedMessageInfo pmi;
+        pmi = sm1.getProcessedMessageInfo(0);
         assertEquals(TEST_CMD_1, pmi.getWhat());
         assertEquals(sm1.mS1, pmi.getState());
         assertEquals(sm1.mS1, pmi.getOriginalState());
 
-        pmi = sm1.getProcessedMessage(1);
+        pmi = sm1.getProcessedMessageInfo(1);
         assertEquals(TEST_CMD_2, pmi.getWhat());
         assertEquals(sm1.mS1, pmi.getState());
         assertEquals(sm1.mS1, pmi.getOriginalState());
@@ -493,7 +505,7 @@ public class HierarchicalStateMachineTest extends TestCase {
      * mS2 then receives both of the deferred messages first TEST_CMD_1 and
      * then TEST_CMD_2.
      */
-    class StateMachine2 extends HierarchicalStateMachine {
+    class StateMachine2 extends StateMachine {
         StateMachine2(String name) {
             super(name);
             mThisSm = this;
@@ -508,26 +520,28 @@ public class HierarchicalStateMachineTest extends TestCase {
             if (DBG) Log.d(TAG, "StateMachine2: ctor X");
         }
 
-        class S1 extends HierarchicalState {
-            @Override protected void enter() {
+        class S1 extends State {
+            @Override
+            public void enter() {
                 mDidEnter = true;
             }
-
-            @Override protected boolean processMessage(Message message) {
+            @Override
+            public void exit() {
+                mDidExit = true;
+            }
+            @Override
+            public boolean processMessage(Message message) {
                 deferMessage(message);
                 if (message.what == TEST_CMD_2) {
                     transitionTo(mS2);
                 }
                 return HANDLED;
             }
-
-            @Override protected void exit() {
-                mDidExit = true;
-            }
         }
 
-        class S2 extends HierarchicalState {
-            @Override protected boolean processMessage(Message message) {
+        class S2 extends State {
+            @Override
+            public boolean processMessage(Message message) {
                 if (message.what == TEST_CMD_2) {
                     transitionToHaltingState();
                 }
@@ -571,20 +585,20 @@ public class HierarchicalStateMachineTest extends TestCase {
 
         assertTrue(sm2.getProcessedMessagesSize() == 4);
 
-        ProcessedMessages.Info pmi;
-        pmi = sm2.getProcessedMessage(0);
+        ProcessedMessageInfo pmi;
+        pmi = sm2.getProcessedMessageInfo(0);
         assertEquals(TEST_CMD_1, pmi.getWhat());
         assertEquals(sm2.mS1, pmi.getState());
 
-        pmi = sm2.getProcessedMessage(1);
+        pmi = sm2.getProcessedMessageInfo(1);
         assertEquals(TEST_CMD_2, pmi.getWhat());
         assertEquals(sm2.mS1, pmi.getState());
 
-        pmi = sm2.getProcessedMessage(2);
+        pmi = sm2.getProcessedMessageInfo(2);
         assertEquals(TEST_CMD_1, pmi.getWhat());
         assertEquals(sm2.mS2, pmi.getState());
 
-        pmi = sm2.getProcessedMessage(3);
+        pmi = sm2.getProcessedMessageInfo(3);
         assertEquals(TEST_CMD_2, pmi.getWhat());
         assertEquals(sm2.mS2, pmi.getState());
 
@@ -598,7 +612,7 @@ public class HierarchicalStateMachineTest extends TestCase {
      * Test that unhandled messages in a child are handled by the parent.
      * When TEST_CMD_2 is received.
      */
-    class StateMachine3 extends HierarchicalStateMachine {
+    class StateMachine3 extends StateMachine {
         StateMachine3(String name) {
             super(name);
             mThisSm = this;
@@ -615,8 +629,9 @@ public class HierarchicalStateMachineTest extends TestCase {
             if (DBG) Log.d(TAG, "StateMachine3: ctor X");
         }
 
-        class ParentState extends HierarchicalState {
-            @Override protected boolean processMessage(Message message) {
+        class ParentState extends State {
+            @Override
+            public boolean processMessage(Message message) {
                 if (message.what == TEST_CMD_2) {
                     transitionToHaltingState();
                 }
@@ -624,8 +639,9 @@ public class HierarchicalStateMachineTest extends TestCase {
             }
         }
 
-        class ChildState extends HierarchicalState {
-            @Override protected boolean processMessage(Message message) {
+        class ChildState extends State {
+            @Override
+            public boolean processMessage(Message message) {
                 return NOT_HANDLED;
             }
         }
@@ -663,13 +679,13 @@ public class HierarchicalStateMachineTest extends TestCase {
 
         assertTrue(sm3.getProcessedMessagesSize() == 2);
 
-        ProcessedMessages.Info pmi;
-        pmi = sm3.getProcessedMessage(0);
+        ProcessedMessageInfo pmi;
+        pmi = sm3.getProcessedMessageInfo(0);
         assertEquals(TEST_CMD_1, pmi.getWhat());
         assertEquals(sm3.mParentState, pmi.getState());
         assertEquals(sm3.mChildState, pmi.getOriginalState());
 
-        pmi = sm3.getProcessedMessage(1);
+        pmi = sm3.getProcessedMessageInfo(1);
         assertEquals(TEST_CMD_2, pmi.getWhat());
         assertEquals(sm3.mParentState, pmi.getState());
         assertEquals(sm3.mChildState, pmi.getOriginalState());
@@ -682,7 +698,7 @@ public class HierarchicalStateMachineTest extends TestCase {
      * with transition from child 1 to child 2 and child 2
      * lets the parent handle the messages.
      */
-    class StateMachine4 extends HierarchicalStateMachine {
+    class StateMachine4 extends StateMachine {
         StateMachine4(String name) {
             super(name);
             mThisSm = this;
@@ -700,8 +716,9 @@ public class HierarchicalStateMachineTest extends TestCase {
             if (DBG) Log.d(TAG, "StateMachine4: ctor X");
         }
 
-        class ParentState extends HierarchicalState {
-            @Override protected boolean processMessage(Message message) {
+        class ParentState extends State {
+            @Override
+            public boolean processMessage(Message message) {
                 if (message.what == TEST_CMD_2) {
                     transitionToHaltingState();
                 }
@@ -709,15 +726,17 @@ public class HierarchicalStateMachineTest extends TestCase {
             }
         }
 
-        class ChildState1 extends HierarchicalState {
-            @Override protected boolean processMessage(Message message) {
+        class ChildState1 extends State {
+            @Override
+            public boolean processMessage(Message message) {
                 transitionTo(mChildState2);
                 return HANDLED;
             }
         }
 
-        class ChildState2 extends HierarchicalState {
-            @Override protected boolean processMessage(Message message) {
+        class ChildState2 extends State {
+            @Override
+            public boolean processMessage(Message message) {
                 return NOT_HANDLED;
             }
         }
@@ -757,13 +776,13 @@ public class HierarchicalStateMachineTest extends TestCase {
 
         assertTrue(sm4.getProcessedMessagesSize() == 2);
 
-        ProcessedMessages.Info pmi;
-        pmi = sm4.getProcessedMessage(0);
+        ProcessedMessageInfo pmi;
+        pmi = sm4.getProcessedMessageInfo(0);
         assertEquals(TEST_CMD_1, pmi.getWhat());
         assertEquals(sm4.mChildState1, pmi.getState());
         assertEquals(sm4.mChildState1, pmi.getOriginalState());
 
-        pmi = sm4.getProcessedMessage(1);
+        pmi = sm4.getProcessedMessageInfo(1);
         assertEquals(TEST_CMD_2, pmi.getWhat());
         assertEquals(sm4.mParentState, pmi.getState());
         assertEquals(sm4.mChildState2, pmi.getOriginalState());
@@ -775,7 +794,7 @@ public class HierarchicalStateMachineTest extends TestCase {
      * Test transition from one child to another of a "complex"
      * hierarchy with two parents and multiple children.
      */
-    class StateMachine5 extends HierarchicalStateMachine {
+    class StateMachine5 extends StateMachine {
         StateMachine5(String name) {
             super(name);
             mThisSm = this;
@@ -797,23 +816,32 @@ public class HierarchicalStateMachineTest extends TestCase {
             if (DBG) Log.d(TAG, "StateMachine5: ctor X");
         }
 
-        class ParentState1 extends HierarchicalState {
-            @Override protected void enter() {
+        class ParentState1 extends State {
+            @Override
+            public void enter() {
                 mParentState1EnterCount += 1;
             }
-            @Override protected boolean processMessage(Message message) {
-                return HANDLED;
-            }
-            @Override protected void exit() {
+            @Override
+            public void exit() {
                 mParentState1ExitCount += 1;
             }
+            @Override
+            public boolean processMessage(Message message) {
+                return HANDLED;
+            }
         }
 
-        class ChildState1 extends HierarchicalState {
-            @Override protected void enter() {
+        class ChildState1 extends State {
+            @Override
+            public void enter() {
                 mChildState1EnterCount += 1;
             }
-            @Override protected boolean processMessage(Message message) {
+            @Override
+            public void exit() {
+                mChildState1ExitCount += 1;
+            }
+            @Override
+            public boolean processMessage(Message message) {
                 assertEquals(1, mParentState1EnterCount);
                 assertEquals(0, mParentState1ExitCount);
                 assertEquals(1, mChildState1EnterCount);
@@ -832,16 +860,19 @@ public class HierarchicalStateMachineTest extends TestCase {
                 transitionTo(mChildState2);
                 return HANDLED;
             }
-            @Override protected void exit() {
-                mChildState1ExitCount += 1;
-            }
         }
 
-        class ChildState2 extends HierarchicalState {
-            @Override protected void enter() {
+        class ChildState2 extends State {
+            @Override
+            public void enter() {
                 mChildState2EnterCount += 1;
             }
-            @Override protected boolean processMessage(Message message) {
+            @Override
+            public void exit() {
+                mChildState2ExitCount += 1;
+            }
+            @Override
+            public boolean processMessage(Message message) {
                 assertEquals(1, mParentState1EnterCount);
                 assertEquals(0, mParentState1ExitCount);
                 assertEquals(1, mChildState1EnterCount);
@@ -860,16 +891,19 @@ public class HierarchicalStateMachineTest extends TestCase {
                 transitionTo(mChildState5);
                 return HANDLED;
             }
-            @Override protected void exit() {
-                mChildState2ExitCount += 1;
-            }
         }
 
-        class ParentState2 extends HierarchicalState {
-            @Override protected void enter() {
+        class ParentState2 extends State {
+            @Override
+            public void enter() {
                 mParentState2EnterCount += 1;
             }
-            @Override protected boolean processMessage(Message message) {
+            @Override
+            public void exit() {
+                mParentState2ExitCount += 1;
+            }
+            @Override
+            public boolean processMessage(Message message) {
                 assertEquals(1, mParentState1EnterCount);
                 assertEquals(1, mParentState1ExitCount);
                 assertEquals(1, mChildState1EnterCount);
@@ -888,16 +922,19 @@ public class HierarchicalStateMachineTest extends TestCase {
                 transitionToHaltingState();
                 return HANDLED;
             }
-            @Override protected void exit() {
-                mParentState2ExitCount += 1;
-            }
         }
 
-        class ChildState3 extends HierarchicalState {
-            @Override protected void enter() {
+        class ChildState3 extends State {
+            @Override
+            public void enter() {
                 mChildState3EnterCount += 1;
             }
-            @Override protected boolean processMessage(Message message) {
+            @Override
+            public void exit() {
+                mChildState3ExitCount += 1;
+            }
+            @Override
+            public boolean processMessage(Message message) {
                 assertEquals(1, mParentState1EnterCount);
                 assertEquals(1, mParentState1ExitCount);
                 assertEquals(1, mChildState1EnterCount);
@@ -916,16 +953,19 @@ public class HierarchicalStateMachineTest extends TestCase {
                 transitionTo(mChildState4);
                 return HANDLED;
             }
-            @Override protected void exit() {
-                mChildState3ExitCount += 1;
-            }
         }
 
-        class ChildState4 extends HierarchicalState {
-            @Override protected void enter() {
+        class ChildState4 extends State {
+            @Override
+            public void enter() {
                 mChildState4EnterCount += 1;
             }
-            @Override protected boolean processMessage(Message message) {
+            @Override
+            public void exit() {
+                mChildState4ExitCount += 1;
+            }
+            @Override
+            public boolean processMessage(Message message) {
                 assertEquals(1, mParentState1EnterCount);
                 assertEquals(1, mParentState1ExitCount);
                 assertEquals(1, mChildState1EnterCount);
@@ -944,16 +984,19 @@ public class HierarchicalStateMachineTest extends TestCase {
                 transitionTo(mParentState2);
                 return HANDLED;
             }
-            @Override protected void exit() {
-                mChildState4ExitCount += 1;
-            }
         }
 
-        class ChildState5 extends HierarchicalState {
-            @Override protected void enter() {
+        class ChildState5 extends State {
+            @Override
+            public void enter() {
                 mChildState5EnterCount += 1;
             }
-            @Override protected boolean processMessage(Message message) {
+            @Override
+            public void exit() {
+                mChildState5ExitCount += 1;
+            }
+            @Override
+            public boolean processMessage(Message message) {
                 assertEquals(1, mParentState1EnterCount);
                 assertEquals(1, mParentState1ExitCount);
                 assertEquals(1, mChildState1EnterCount);
@@ -972,9 +1015,6 @@ public class HierarchicalStateMachineTest extends TestCase {
                 transitionTo(mChildState3);
                 return HANDLED;
             }
-            @Override protected void exit() {
-                mChildState5ExitCount += 1;
-            }
         }
 
         @Override
@@ -1050,33 +1090,33 @@ public class HierarchicalStateMachineTest extends TestCase {
         assertEquals(1, sm5.mChildState5EnterCount);
         assertEquals(1, sm5.mChildState5ExitCount);
 
-        ProcessedMessages.Info pmi;
-        pmi = sm5.getProcessedMessage(0);
+        ProcessedMessageInfo pmi;
+        pmi = sm5.getProcessedMessageInfo(0);
         assertEquals(TEST_CMD_1, pmi.getWhat());
         assertEquals(sm5.mChildState1, pmi.getState());
         assertEquals(sm5.mChildState1, pmi.getOriginalState());
 
-        pmi = sm5.getProcessedMessage(1);
+        pmi = sm5.getProcessedMessageInfo(1);
         assertEquals(TEST_CMD_2, pmi.getWhat());
         assertEquals(sm5.mChildState2, pmi.getState());
         assertEquals(sm5.mChildState2, pmi.getOriginalState());
 
-        pmi = sm5.getProcessedMessage(2);
+        pmi = sm5.getProcessedMessageInfo(2);
         assertEquals(TEST_CMD_3, pmi.getWhat());
         assertEquals(sm5.mChildState5, pmi.getState());
         assertEquals(sm5.mChildState5, pmi.getOriginalState());
 
-        pmi = sm5.getProcessedMessage(3);
+        pmi = sm5.getProcessedMessageInfo(3);
         assertEquals(TEST_CMD_4, pmi.getWhat());
         assertEquals(sm5.mChildState3, pmi.getState());
         assertEquals(sm5.mChildState3, pmi.getOriginalState());
 
-        pmi = sm5.getProcessedMessage(4);
+        pmi = sm5.getProcessedMessageInfo(4);
         assertEquals(TEST_CMD_5, pmi.getWhat());
         assertEquals(sm5.mChildState4, pmi.getState());
         assertEquals(sm5.mChildState4, pmi.getOriginalState());
 
-        pmi = sm5.getProcessedMessage(5);
+        pmi = sm5.getProcessedMessageInfo(5);
         assertEquals(TEST_CMD_6, pmi.getWhat());
         assertEquals(sm5.mParentState2, pmi.getState());
         assertEquals(sm5.mParentState2, pmi.getOriginalState());
@@ -1089,7 +1129,7 @@ public class HierarchicalStateMachineTest extends TestCase {
      * after construction and before any other messages arrive and that
      * sendMessageDelayed works.
      */
-    class StateMachine6 extends HierarchicalStateMachine {
+    class StateMachine6 extends StateMachine {
         StateMachine6(String name) {
             super(name);
             mThisSm = this;
@@ -1103,13 +1143,13 @@ public class HierarchicalStateMachineTest extends TestCase {
             if (DBG) Log.d(TAG, "StateMachine6: ctor X");
         }
 
-        class S1 extends HierarchicalState {
-
-            @Override protected void enter() {
+        class S1 extends State {
+            @Override
+            public void enter() {
                 sendMessage(TEST_CMD_1);
             }
-
-            @Override protected boolean processMessage(Message message) {
+            @Override
+            public boolean processMessage(Message message) {
                 if (message.what == TEST_CMD_1) {
                     mArrivalTimeMsg1 = SystemClock.elapsedRealtime();
                 } else if (message.what == TEST_CMD_2) {
@@ -1118,9 +1158,6 @@ public class HierarchicalStateMachineTest extends TestCase {
                 }
                 return HANDLED;
             }
-
-            @Override protected void exit() {
-            }
         }
 
         @Override
@@ -1178,7 +1215,7 @@ public class HierarchicalStateMachineTest extends TestCase {
      * Test that enter is invoked immediately after exit. This validates
      * that enter can be used to send a watch dog message for its state.
      */
-    class StateMachine7 extends HierarchicalStateMachine {
+    class StateMachine7 extends StateMachine {
         private final int SM7_DELAY_TIME = 250;
 
         StateMachine7(String name) {
@@ -1195,24 +1232,26 @@ public class HierarchicalStateMachineTest extends TestCase {
             if (DBG) Log.d(TAG, "StateMachine7: ctor X");
         }
 
-        class S1 extends HierarchicalState {
-            @Override protected boolean processMessage(Message message) {
+        class S1 extends State {
+            @Override
+            public void exit() {
+                sendMessage(TEST_CMD_2);
+            }
+            @Override
+            public boolean processMessage(Message message) {
                 transitionTo(mS2);
                 return HANDLED;
             }
-            @Override protected void exit() {
-                sendMessage(TEST_CMD_2);
-            }
         }
 
-        class S2 extends HierarchicalState {
-
-            @Override protected void enter() {
+        class S2 extends State {
+            @Override
+            public void enter() {
                 // Send a delayed message as a watch dog
                 sendMessageDelayed(TEST_CMD_3, SM7_DELAY_TIME);
             }
-
-            @Override protected boolean processMessage(Message message) {
+            @Override
+            public boolean processMessage(Message message) {
                 if (message.what == TEST_CMD_2) {
                     mMsgCount += 1;
                     mArrivalTimeMsg2 = SystemClock.elapsedRealtime();
@@ -1226,9 +1265,6 @@ public class HierarchicalStateMachineTest extends TestCase {
                 }
                 return HANDLED;
             }
-
-            @Override protected void exit() {
-            }
         }
 
         @Override
@@ -1286,7 +1322,7 @@ public class HierarchicalStateMachineTest extends TestCase {
     /**
      * Test unhandledMessage.
      */
-    class StateMachineUnhandledMessage extends HierarchicalStateMachine {
+    class StateMachineUnhandledMessage extends StateMachine {
         StateMachineUnhandledMessage(String name) {
             super(name);
             mThisSm = this;
@@ -1298,13 +1334,14 @@ public class HierarchicalStateMachineTest extends TestCase {
             // Set the initial state
             setInitialState(mS1);
         }
-
-        @Override protected void unhandledMessage(Message message) {
+        @Override
+        public void unhandledMessage(Message message) {
             mUnhandledMessageCount += 1;
         }
 
-        class S1 extends HierarchicalState {
-            @Override protected boolean processMessage(Message message) {
+        class S1 extends State {
+            @Override
+            public boolean processMessage(Message message) {
                 if (message.what == TEST_CMD_2) {
                     transitionToHaltingState();
                 }
@@ -1359,7 +1396,7 @@ public class HierarchicalStateMachineTest extends TestCase {
      * will be used to notify testStateMachineSharedThread that the test is
      * complete.
      */
-    class StateMachineSharedThread extends HierarchicalStateMachine {
+    class StateMachineSharedThread extends StateMachine {
         StateMachineSharedThread(String name, Looper looper, int maxCount) {
             super(name, looper);
             mMaxCount = maxCount;
@@ -1372,8 +1409,9 @@ public class HierarchicalStateMachineTest extends TestCase {
             setInitialState(mS1);
         }
 
-        class S1 extends HierarchicalState {
-            @Override protected boolean processMessage(Message message) {
+        class S1 extends State {
+            @Override
+            public boolean processMessage(Message message) {
                 if (message.what == TEST_CMD_4) {
                     transitionToHaltingState();
                 }
@@ -1434,7 +1472,7 @@ public class HierarchicalStateMachineTest extends TestCase {
         for (StateMachineSharedThread sm : sms) {
             assertTrue(sm.getProcessedMessagesCount() == 4);
             for (int i = 0; i < sm.getProcessedMessagesCount(); i++) {
-                ProcessedMessages.Info pmi = sm.getProcessedMessage(i);
+                ProcessedMessageInfo pmi = sm.getProcessedMessageInfo(i);
                 assertEquals(i+1, pmi.getWhat());
                 assertEquals(sm.mS1, pmi.getState());
                 assertEquals(sm.mS1, pmi.getOriginalState());
@@ -1464,37 +1502,37 @@ public class HierarchicalStateMachineTest extends TestCase {
         }
 
         assertEquals(7, sm.getProcessedMessagesCount());
-        ProcessedMessages.Info pmi = sm.getProcessedMessage(0);
+        ProcessedMessageInfo pmi = sm.getProcessedMessageInfo(0);
         assertEquals(Hsm1.CMD_1, pmi.getWhat());
         assertEquals(sm.mS1, pmi.getState());
         assertEquals(sm.mS1, pmi.getOriginalState());
 
-        pmi = sm.getProcessedMessage(1);
+        pmi = sm.getProcessedMessageInfo(1);
         assertEquals(Hsm1.CMD_2, pmi.getWhat());
         assertEquals(sm.mP1, pmi.getState());
         assertEquals(sm.mS1, pmi.getOriginalState());
 
-        pmi = sm.getProcessedMessage(2);
+        pmi = sm.getProcessedMessageInfo(2);
         assertEquals(Hsm1.CMD_2, pmi.getWhat());
         assertEquals(sm.mS2, pmi.getState());
         assertEquals(sm.mS2, pmi.getOriginalState());
 
-        pmi = sm.getProcessedMessage(3);
+        pmi = sm.getProcessedMessageInfo(3);
         assertEquals(Hsm1.CMD_3, pmi.getWhat());
         assertEquals(sm.mS2, pmi.getState());
         assertEquals(sm.mS2, pmi.getOriginalState());
 
-        pmi = sm.getProcessedMessage(4);
+        pmi = sm.getProcessedMessageInfo(4);
         assertEquals(Hsm1.CMD_3, pmi.getWhat());
         assertEquals(sm.mP2, pmi.getState());
         assertEquals(sm.mP2, pmi.getOriginalState());
 
-        pmi = sm.getProcessedMessage(5);
+        pmi = sm.getProcessedMessageInfo(5);
         assertEquals(Hsm1.CMD_4, pmi.getWhat());
         assertEquals(sm.mP2, pmi.getState());
         assertEquals(sm.mP2, pmi.getOriginalState());
 
-        pmi = sm.getProcessedMessage(6);
+        pmi = sm.getProcessedMessageInfo(6);
         assertEquals(Hsm1.CMD_5, pmi.getWhat());
         assertEquals(sm.mP2, pmi.getState());
         assertEquals(sm.mP2, pmi.getOriginalState());
@@ -1503,7 +1541,7 @@ public class HierarchicalStateMachineTest extends TestCase {
     }
 }
 
-class Hsm1 extends HierarchicalStateMachine {
+class Hsm1 extends StateMachine {
     private static final String TAG = "hsm1";
 
     public static final int CMD_1 = 1;
@@ -1535,11 +1573,17 @@ class Hsm1 extends HierarchicalStateMachine {
         Log.d(TAG, "ctor X");
     }
 
-    class P1 extends HierarchicalState {
-        @Override protected void enter() {
+    class P1 extends State {
+        @Override
+        public void enter() {
             Log.d(TAG, "P1.enter");
         }
-        @Override protected boolean processMessage(Message message) {
+        @Override
+        public void exit() {
+            Log.d(TAG, "P1.exit");
+        }
+        @Override
+        public boolean processMessage(Message message) {
             boolean retVal;
             Log.d(TAG, "P1.processMessage what=" + message.what);
             switch(message.what) {
@@ -1557,16 +1601,19 @@ class Hsm1 extends HierarchicalStateMachine {
             }
             return retVal;
         }
-        @Override protected void exit() {
-            Log.d(TAG, "P1.exit");
-        }
     }
 
-    class S1 extends HierarchicalState {
-        @Override protected void enter() {
+    class S1 extends State {
+        @Override
+        public void enter() {
             Log.d(TAG, "S1.enter");
         }
-        @Override protected boolean processMessage(Message message) {
+        @Override
+        public void exit() {
+            Log.d(TAG, "S1.exit");
+        }
+        @Override
+        public boolean processMessage(Message message) {
             Log.d(TAG, "S1.processMessage what=" + message.what);
             if (message.what == CMD_1) {
                 // Transition to ourself to show that enter/exit is called
@@ -1577,16 +1624,19 @@ class Hsm1 extends HierarchicalStateMachine {
                 return NOT_HANDLED;
             }
         }
-        @Override protected void exit() {
-            Log.d(TAG, "S1.exit");
-        }
     }
 
-    class S2 extends HierarchicalState {
-        @Override protected void enter() {
+    class S2 extends State {
+        @Override
+        public void enter() {
             Log.d(TAG, "S2.enter");
         }
-        @Override protected boolean processMessage(Message message) {
+        @Override
+        public void exit() {
+            Log.d(TAG, "S2.exit");
+        }
+        @Override
+        public boolean processMessage(Message message) {
             boolean retVal;
             Log.d(TAG, "S2.processMessage what=" + message.what);
             switch(message.what) {
@@ -1605,17 +1655,20 @@ class Hsm1 extends HierarchicalStateMachine {
             }
             return retVal;
         }
-        @Override protected void exit() {
-            Log.d(TAG, "S2.exit");
-        }
     }
 
-    class P2 extends HierarchicalState {
-        @Override protected void enter() {
+    class P2 extends State {
+        @Override
+        public void enter() {
             Log.d(TAG, "P2.enter");
             sendMessage(CMD_5);
         }
-        @Override protected boolean processMessage(Message message) {
+        @Override
+        public void exit() {
+            Log.d(TAG, "P2.exit");
+        }
+        @Override
+        public boolean processMessage(Message message) {
             Log.d(TAG, "P2.processMessage what=" + message.what);
             switch(message.what) {
             case(CMD_3):
@@ -1628,9 +1681,6 @@ class Hsm1 extends HierarchicalStateMachine {
             }
             return HANDLED;
         }
-        @Override protected void exit() {
-            Log.d(TAG, "P2.exit");
-        }
     }
 
     @Override
index 1870a4a..5ed7966 100644 (file)
         <group gid="mtp" />
     </permission>
 
+    <permission name="android.permission.NET_ADMIN" >
+        <group gid="net_admin" />
+    </permission>
+
     <!-- The group that /cache belongs to, linked to the permission
          set on the applications that can access /cache -->
     <permission name="android.permission.ACCESS_CACHE_FILESYSTEM" >
index 498c7b8..2caf211 100644 (file)
@@ -220,40 +220,58 @@ status_t HTTPStream::connect(const char *server, int port, bool https) {
         return ERROR_ALREADY_CONNECTED;
     }
 
-    struct hostent *ent = gethostbyname(server);
-    if (ent == NULL) {
-        return ERROR_UNKNOWN_HOST;
+    if (port < 0 || port > (int) USHRT_MAX) {
+        return UNKNOWN_ERROR;
     }
 
-    CHECK_EQ(mSocket, -1);
-    mSocket = socket(AF_INET, SOCK_STREAM, 0);
+    char service[sizeof("65536")];
+    sprintf(service, "%d", port);
+    struct addrinfo hints, *ai;
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
+    hints.ai_socktype = SOCK_STREAM;
 
-    if (mSocket < 0) {
-        return UNKNOWN_ERROR;
+    int ret = getaddrinfo(server, service, &hints, &ai);
+    if (ret) {
+        return ERROR_UNKNOWN_HOST;
     }
 
-    setReceiveTimeout(30);  // Time out reads after 30 secs by default
+    CHECK_EQ(mSocket, -1);
 
     mState = CONNECTING;
+    status_t res = -1;
+    struct addrinfo *tmp;
+    for (tmp = ai; tmp; tmp = tmp->ai_next) {
+        mSocket = socket(tmp->ai_family, tmp->ai_socktype, tmp->ai_protocol);
+        if (mSocket < 0) {
+            continue;
+        }
 
-    int s = mSocket;
+        setReceiveTimeout(30);  // Time out reads after 30 secs by default.
 
-    mLock.unlock();
+        int s = mSocket;
 
-    struct sockaddr_in addr;
-    addr.sin_family = AF_INET;
-    addr.sin_port = htons(port);
-    addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
-    memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
+        mLock.unlock();
 
-    status_t res = MyConnect(s, (const struct sockaddr *)&addr, sizeof(addr));
+        res = MyConnect(s, tmp->ai_addr, tmp->ai_addrlen);
 
-    mLock.lock();
+        mLock.lock();
 
-    if (mState != CONNECTING) {
-        return UNKNOWN_ERROR;
+        if (mState != CONNECTING) {
+            close(s);
+            freeaddrinfo(ai);
+            return UNKNOWN_ERROR;
+        }
+
+        if (res == OK) {
+            break;
+        }
+
+        close(s);
     }
 
+    freeaddrinfo(ai);
+
     if (res != OK) {
         close(mSocket);
         mSocket = -1;
index 7802efd..81c6167 100644 (file)
@@ -22,11 +22,6 @@ LOCAL_SRC_FILES := \
         SkOmxPixelRef.cpp \
         StreamSource.cpp
 
-
-# add external/skia/src/images/SkImageDecoder_libjpeg.cpp
-LOCAL_SRC_FILES += \
-        ../../../../../external/skia/src/images/SkImageDecoder_libjpeg.cpp
-
 LOCAL_SHARED_LIBRARIES := \
     libcutils \
     libskia \
diff --git a/nfc-extras/Android.mk b/nfc-extras/Android.mk
new file mode 100644 (file)
index 0000000..131d898
--- /dev/null
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_MODULE:= com.android.nfc_extras
+
+include $(BUILD_JAVA_LIBRARY)
+
+# put the classes.jar, with full class files instead of classes.dex inside, into the dist directory
+$(call dist-for-goals, droidcore, $(full_classes_jar):com.android.nfc_extras.jar)
diff --git a/nfc-extras/com.android.nfc_extras.xml b/nfc-extras/com.android.nfc_extras.xml
new file mode 100644 (file)
index 0000000..370145d
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<permissions>
+    <library name="com.android.nfc_extras"
+            file="/system/framework/com.android.nfc_extras.jar" />
+</permissions>
diff --git a/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java b/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java
new file mode 100644 (file)
index 0000000..cf38bd1
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2011 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.nfc_extras;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.nfc.INfcAdapterExtras;
+import android.nfc.NfcAdapter;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Provides additional methods on an {@link NfcAdapter} for Card Emulation
+ * and management of {@link NfcExecutionEnvironment}'s.
+ *
+ * There is a 1-1 relationship between an {@link NfcAdapterExtras} object and
+ * a {@link NfcAdapter} object.
+ */
+public final class NfcAdapterExtras {
+    private static final String TAG = "NfcAdapterExtras";
+
+    /**
+     * Broadcast Action: an RF field ON has been detected.
+     *
+     * <p class="note">This is an unreliable signal, and will be removed.
+     * <p class="note">
+     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission
+     * to receive.
+     */
+    public static final String ACTION_RF_FIELD_ON_DETECTED =
+            "com.android.nfc_extras.action.RF_FIELD_ON_DETECTED";
+
+    /**
+     * Broadcast Action: an RF field OFF has been detected.
+     *
+     * <p class="note">This is an unreliable signal, and will be removed.
+     * <p class="note">
+     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission
+     * to receive.
+     */
+    public static final String ACTION_RF_FIELD_OFF_DETECTED =
+            "com.android.nfc_extras.action.RF_FIELD_OFF_DETECTED";
+
+    // protected by NfcAdapterExtras.class, and final after first construction
+    private static INfcAdapterExtras sService;
+    private static boolean sIsInitialized = false;
+    private static NfcAdapterExtras sSingleton;
+    private static NfcExecutionEnvironment sEmbeddedEe;
+    private static CardEmulationRoute sRouteOff;
+    private static CardEmulationRoute sRouteOnWhenScreenOn;
+
+    /**
+     * Get the {@link NfcAdapterExtras} for the given {@link NfcAdapter}.
+     *
+     * <p class="note">
+     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
+     *
+     * @param adapter a {@link NfcAdapter}, must not be null
+     * @return the {@link NfcAdapterExtras} object for the given {@link NfcAdapter}
+     */
+    public static NfcAdapterExtras get(NfcAdapter adapter) {
+        synchronized(NfcAdapterExtras.class) {
+            if (!sIsInitialized) {
+               sIsInitialized = true;
+               sService = adapter.getNfcAdapterExtrasInterface();
+               sEmbeddedEe = new NfcExecutionEnvironment(sService);
+               sRouteOff = new CardEmulationRoute(CardEmulationRoute.ROUTE_OFF, null);
+               sRouteOnWhenScreenOn = new CardEmulationRoute(
+                       CardEmulationRoute.ROUTE_ON_WHEN_SCREEN_ON, sEmbeddedEe);
+               sSingleton = new NfcAdapterExtras();
+            }
+            return sSingleton;
+        }
+    }
+
+    private NfcAdapterExtras() {}
+
+    /**
+     * Immutable data class that describes a card emulation route.
+     */
+    public final static class CardEmulationRoute {
+        /**
+         * Card Emulation is turned off on this NfcAdapter.
+         * <p>This is the default routing state after boot.
+         */
+        public static final int ROUTE_OFF = 1;
+
+        /**
+         * Card Emulation is routed to {@link #nfcEe} only when the screen is on,
+         * otherwise it is turned off.
+         */
+        public static final int ROUTE_ON_WHEN_SCREEN_ON = 2;
+
+        /**
+         * A route such as {@link #ROUTE_OFF} or {@link #ROUTE_ON_WHEN_SCREEN_ON}.
+         */
+        public final int route;
+
+        /**
+         * The {@link NFcExecutionEnvironment} that is Card Emulation is routed to.
+         * <p>null if {@link #route} is {@link #ROUTE_OFF}, otherwise not null.
+         */
+        public final NfcExecutionEnvironment nfcEe;
+
+        public CardEmulationRoute(int route, NfcExecutionEnvironment nfcEe) {
+            if (route == ROUTE_OFF && nfcEe != null) {
+                throw new IllegalArgumentException("must not specifiy a NFC-EE with ROUTE_OFF");
+            } else if (route != ROUTE_OFF && nfcEe == null) {
+                throw new IllegalArgumentException("must specifiy a NFC-EE for this route");
+            }
+            this.route = route;
+            this.nfcEe = nfcEe;
+        }
+    }
+
+    /**
+     * Get the routing state of this NFC EE.
+     *
+     * <p class="note">
+     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
+     *
+     * @return
+     */
+    public CardEmulationRoute getCardEmulationRoute() {
+        try {
+            int route = sService.getCardEmulationRoute();
+            return route == CardEmulationRoute.ROUTE_OFF ?
+                    sRouteOff :
+                    sRouteOnWhenScreenOn;
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+            return sRouteOff;
+        }
+    }
+
+    /**
+     * Set the routing state of this NFC EE.
+     *
+     * <p>This routing state is not persisted across reboot.
+     *
+     * <p class="note">
+     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
+     *
+     * @param route a {@link #CardEmulationRoute}
+     */
+    public void setCardEmulationRoute(CardEmulationRoute route) {
+        try {
+            sService.setCardEmulationRoute(route.route);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        }
+    }
+
+    /**
+     * Get the {@link NfcExecutionEnvironment} that is embedded with the
+     * {@link NFcAdapter}.
+     *
+     * <p class="note">
+     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
+     *
+     * @return a {@link NfcExecutionEnvironment}, or null if there is no embedded NFC-EE
+     */
+    public NfcExecutionEnvironment getEmbeddedExecutionEnvironment() {
+        return sEmbeddedEe;
+    }
+}
diff --git a/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java b/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java
new file mode 100644 (file)
index 0000000..3efe492
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2011 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.nfc_extras;
+
+import java.io.IOException;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.Context;
+import android.nfc.INfcAdapterExtras;
+import android.nfc.NfcAdapter;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+public class NfcExecutionEnvironment {
+    private final INfcAdapterExtras mService;
+
+    /**
+     * Broadcast Action: An ISO-DEP AID was selected.
+     *
+     * <p>This happens as the result of a 'SELECT AID' command from an
+     * external NFC reader/writer.
+     *
+     * <p>Always contains the extra field {@link #EXTRA_AID}
+     *
+     * <p class="note">
+     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission
+     * to receive.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_AID_SELECTED =
+        "com.android.nfc_extras.action.AID_SELECTED";
+
+    /**
+     * Mandatory byte array extra field in {@link #ACTION_AID_SELECTED}.
+     *
+     * <p>Contains the AID selected.
+     * @hide
+     */
+    public static final String EXTRA_AID = "com.android.nfc_extras.extra.AID";
+
+    NfcExecutionEnvironment(INfcAdapterExtras service) {
+        mService = service;
+    }
+
+    /**
+     * Open the NFC Execution Environment on its contact interface.
+     *
+     * <p>Only one process may open the secure element at a time. If it is
+     * already open, an {@link IOException} is thrown.
+     *
+     * <p>All other NFC functionality is disabled while the NFC-EE is open
+     * on its contact interface, so make sure to call {@link #close} once complete.
+     *
+     * <p class="note">
+     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
+     *
+     * @throws IOException if the NFC-EE is already open, or some other error occurs
+     */
+    public void open() throws IOException {
+        try {
+            Bundle b = mService.open(new Binder());
+            throwBundle(b);
+        } catch (RemoteException e) {
+            return;
+        }
+    }
+
+    /**
+     * Close the NFC Execution Environment on its contact interface.
+     *
+     * <p class="note">
+     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
+     *
+     * @throws IOException if the NFC-EE is already open, or some other error occurs
+     */
+    public void close() throws IOException {
+        try {
+            throwBundle(mService.close());
+        } catch (RemoteException e) {
+            return;
+        }
+    }
+
+    /**
+     * Send raw commands to the NFC-EE and receive the response.
+     *
+     * <p class="note">
+     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
+     *
+     * @throws IOException if the NFC-EE is not open, or some other error occurs
+     */
+    public byte[] transceive(byte[] in) throws IOException {
+        Bundle b;
+        try {
+            b = mService.transceive(in);
+        } catch (RemoteException e) {
+            throw new IOException(e.getMessage());
+        }
+        throwBundle(b);
+        return b.getByteArray("out");
+    }
+
+    private static void throwBundle(Bundle b) throws IOException {
+        if (b.getInt("e") == -1) {
+            throw new IOException(b.getString("m"));
+        }
+    }
+}
index 2ed968b..964fe9c 100644 (file)
@@ -17,6 +17,8 @@
 package com.android.providers.settings;
 
 import com.android.internal.content.PackageHelper;
+import com.android.internal.telephony.BaseCommands;
+import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.widget.LockPatternUtils;
@@ -1300,8 +1302,13 @@ public class DatabaseHelper extends SQLiteOpenHelper {
             }
     
             // Set the preferred network mode to 0 = Global, CDMA default
-            int type = SystemProperties.getInt("ro.telephony.default_network",
-                    RILConstants.PREFERRED_NETWORK_MODE);
+            int type;
+            if (BaseCommands.getLteOnCdmaModeStatic() == Phone.LTE_ON_CDMA_TRUE) {
+                type = Phone.NT_MODE_GLOBAL;
+            } else {
+                type = SystemProperties.getInt("ro.telephony.default_network",
+                        RILConstants.PREFERRED_NETWORK_MODE);
+            }
             loadSetting(stmt, Settings.Secure.PREFERRED_NETWORK_MODE, type);
     
             // Enable or disable Cell Broadcast SMS
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4g.png
new file mode 100644 (file)
index 0000000..ade3716
Binary files /dev/null and b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4g.png differ
index 0273a4c..826ac92 100644 (file)
@@ -246,6 +246,16 @@ public class PhoneStatusBarPolicy {
               R.drawable.stat_sys_data_fully_out_3g,
               R.drawable.stat_sys_data_fully_inandout_3g }
         };
+    private static final int[][] sDataNetType_4g = {
+            { R.drawable.stat_sys_data_connected_4g,
+              R.drawable.stat_sys_data_in_4g,
+              R.drawable.stat_sys_data_out_4g,
+              R.drawable.stat_sys_data_inandout_4g },
+            { R.drawable.stat_sys_data_fully_connected_4g,
+              R.drawable.stat_sys_data_fully_in_4g,
+              R.drawable.stat_sys_data_fully_out_4g,
+              R.drawable.stat_sys_data_fully_inandout_4g }
+        };
     private static final int[][] sDataNetType_e = {
             { R.drawable.stat_sys_data_connected_e,
               R.drawable.stat_sys_data_in_e,
@@ -670,9 +680,12 @@ public class PhoneStatusBarPolicy {
         case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through
         case TelephonyManager.NETWORK_TYPE_EVDO_A:
         case TelephonyManager.NETWORK_TYPE_EVDO_B:
+        case TelephonyManager.NETWORK_TYPE_EHRPD:
             mDataIconList = sDataNetType_3g[mInetCondition];
             break;
-        // TODO - add support for NETWORK_TYPE_LTE and NETWORK_TYPE_EHRPD
+        case TelephonyManager.NETWORK_TYPE_LTE:
+            mDataIconList = sDataNetType_4g[mInetCondition];
+            break;
         default:
             mDataIconList = sDataNetType_g[mInetCondition];
         break;
index b5e7920..3175a99 100644 (file)
@@ -427,10 +427,14 @@ public class NetworkController extends BroadcastReceiver {
             case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through
             case TelephonyManager.NETWORK_TYPE_EVDO_A:
             case TelephonyManager.NETWORK_TYPE_EVDO_B:
+            case TelephonyManager.NETWORK_TYPE_EHRPD:
                 mDataIconList = TelephonyIcons.DATA_3G[mInetCondition];
                 mDataTypeIconId = R.drawable.stat_sys_signal_3g;
                 break;
-            // TODO - add support for NETWORK_TYPE_LTE and NETWORK_TYPE_EHRPD
+            case TelephonyManager.NETWORK_TYPE_LTE:
+                mDataIconList = TelephonyIcons.DATA_4G[mInetCondition];
+                mDataTypeIconId = R.drawable.stat_sys_signal_4g;
+                break;
             default:
                 mDataIconList = TelephonyIcons.DATA_G[mInetCondition];
                 mDataTypeIconId = R.drawable.stat_sys_signal_gprs;
index 29d35e3..9093b3e 100644 (file)
@@ -124,5 +124,18 @@ class TelephonyIcons {
               R.drawable.stat_sys_data_fully_inandout_1x }
             };
 
+    // LTE and eHRPD
+    static final int[][] DATA_4G = {
+            { R.drawable.stat_sys_data_connected_4g,
+              R.drawable.stat_sys_data_in_4g,
+              R.drawable.stat_sys_data_out_4g,
+              R.drawable.stat_sys_data_inandout_4g },
+            { R.drawable.stat_sys_data_fully_connected_4g,
+              R.drawable.stat_sys_data_fully_in_4g,
+              R.drawable.stat_sys_data_fully_out_4g,
+              R.drawable.stat_sys_data_fully_inandout_4g }
+        };
+
+
 }
 
index 5ed67a9..f385a23 100644 (file)
@@ -114,7 +114,15 @@ public class KeyguardUpdateMonitor {
             }
             String stateExtra = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE);
             if (IccCard.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
-                this.simState = IccCard.State.ABSENT;
+                final String absentReason = intent
+                    .getStringExtra(IccCard.INTENT_KEY_LOCKED_REASON);
+
+                if (IccCard.INTENT_VALUE_ABSENT_ON_PERM_DISABLED.equals(
+                        absentReason)) {
+                    this.simState = IccCard.State.PERM_DISABLED;
+                } else {
+                    this.simState = IccCard.State.ABSENT;
+                }
             } else if (IccCard.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
                 this.simState = IccCard.State.READY;
             } else if (IccCard.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
@@ -480,11 +488,11 @@ public class KeyguardUpdateMonitor {
     }
 
     /**
-     * Report that the user succesfully entered the sim pin so we
+     * Report that the user succesfully entered the sim pin or puk so we
      * have the information earlier than waiting for the intent
      * broadcast from the telephony code.
      */
-    public void reportSimPinUnlocked() {
+    public void reportSimUnlocked() {
         mSimState = IccCard.State.READY;
     }
 
index e7a9eb1..cfd0d4f 100644 (file)
@@ -581,7 +581,9 @@ public class KeyguardViewMediator implements KeyguardViewCallback,
             final boolean provisioned = mUpdateMonitor.isDeviceProvisioned();
             final IccCard.State state = mUpdateMonitor.getSimState();
             final boolean lockedOrMissing = state.isPinLocked()
-                    || ((state == IccCard.State.ABSENT) && requireSim);
+                    || ((state == IccCard.State.ABSENT
+                            || state == IccCard.State.PERM_DISABLED)
+                            && requireSim);
 
             if (!lockedOrMissing && !provisioned) {
                 if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
@@ -688,12 +690,15 @@ public class KeyguardViewMediator implements KeyguardViewCallback,
 
         switch (simState) {
             case ABSENT:
+            case PERM_DISABLED:
                 // only force lock screen in case of missing sim if user hasn't
                 // gone through setup wizard
                 if (!mUpdateMonitor.isDeviceProvisioned()) {
                     if (!isShowing()) {
-                        if (DEBUG) Log.d(TAG, "INTENT_VALUE_ICC_ABSENT and keygaurd isn't showing, we need "
-                             + "to show the keyguard since the device isn't provisioned yet.");
+                        if (DEBUG) Log.d(TAG, "INTENT_VALUE_ICC_ABSENT "
+                                + "or PERM_DISABLED and keygaurd isn't showing,"
+                                + " we need to show the keyguard since the "
+                                + "device isn't provisioned yet.");
                         doKeyguard();
                     } else {
                         resetStateLocked();
index 2fda3aa..eea3040 100644 (file)
@@ -116,6 +116,11 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
         SimPin,
 
         /**
+         * Unlock by entering a sim puk.
+         */
+        SimPuk,
+
+        /**
          * Unlock by entering an account's login and password.
          */
         Account,
@@ -176,7 +181,8 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
     private boolean stuckOnLockScreenBecauseSimMissing() {
         return mRequiresSim
                 && (!mUpdateMonitor.isDeviceProvisioned())
-                && (mUpdateMonitor.getSimState() == IccCard.State.ABSENT);
+                && (mUpdateMonitor.getSimState() == IccCard.State.ABSENT ||
+                    mUpdateMonitor.getSimState() == IccCard.State.PERM_DISABLED);
     }
 
     /**
@@ -222,8 +228,10 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
             public void goToUnlockScreen() {
                 final IccCard.State simState = mUpdateMonitor.getSimState();
                 if (stuckOnLockScreenBecauseSimMissing()
-                         || (simState == IccCard.State.PUK_REQUIRED)){
-                    // stuck on lock screen when sim missing or puk'd
+                         || (simState == IccCard.State.PUK_REQUIRED
+                             && !mLockPatternUtils.isPukUnlockScreenEnable())){
+                    // stuck on lock screen when sim missing or
+                    // puk'd but puk unlock screen is disabled
                     return;
                 }
                 if (!isSecure()) {
@@ -522,8 +530,10 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
                 secure = mLockPatternUtils.isLockPatternEnabled();
                 break;
             case SimPin:
-                secure = mUpdateMonitor.getSimState() == IccCard.State.PIN_REQUIRED
-                            || mUpdateMonitor.getSimState() == IccCard.State.PUK_REQUIRED;
+                secure = mUpdateMonitor.getSimState() == IccCard.State.PIN_REQUIRED;
+                break;
+            case SimPuk:
+                secure = mUpdateMonitor.getSimState() == IccCard.State.PUK_REQUIRED;
                 break;
             case Account:
                 secure = true;
@@ -592,6 +602,10 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
 
     View createUnlockScreenFor(UnlockMode unlockMode) {
         View unlockView = null;
+
+        if (DEBUG) Log.d(TAG,
+                "createUnlockScreenFor(" + unlockMode + "): mEnableFallback=" + mEnableFallback);
+
         if (unlockMode == UnlockMode.Pattern) {
             PatternUnlockScreen view = new PatternUnlockScreen(
                     mContext,
@@ -600,10 +614,15 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
                     mUpdateMonitor,
                     mKeyguardScreenCallback,
                     mUpdateMonitor.getFailedAttempts());
-            if (DEBUG) Log.d(TAG,
-                "createUnlockScreenFor(" + unlockMode + "): mEnableFallback=" + mEnableFallback);
             view.setEnableFallback(mEnableFallback);
             unlockView = view;
+        } else if (unlockMode == UnlockMode.SimPuk) {
+            unlockView = new SimPukUnlockScreen(
+                    mContext,
+                    mConfiguration,
+                    mUpdateMonitor,
+                    mKeyguardScreenCallback,
+                    mLockPatternUtils);
         } else if (unlockMode == UnlockMode.SimPin) {
             unlockView = new SimUnlockScreen(
                     mContext,
@@ -654,7 +673,9 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
      */
     private Mode getInitialMode() {
         final IccCard.State simState = mUpdateMonitor.getSimState();
-        if (stuckOnLockScreenBecauseSimMissing() || (simState == IccCard.State.PUK_REQUIRED)) {
+        if (stuckOnLockScreenBecauseSimMissing() ||
+                (simState == IccCard.State.PUK_REQUIRED &&
+                        !mLockPatternUtils.isPukUnlockScreenEnable())) {
             return Mode.LockScreen;
         } else {
             // Show LockScreen first for any screen other than Pattern unlock.
@@ -676,8 +697,10 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
     private UnlockMode getUnlockMode() {
         final IccCard.State simState = mUpdateMonitor.getSimState();
         UnlockMode currentMode;
-        if (simState == IccCard.State.PIN_REQUIRED || simState == IccCard.State.PUK_REQUIRED) {
+        if (simState == IccCard.State.PIN_REQUIRED) {
             currentMode = UnlockMode.SimPin;
+        } else if (simState == IccCard.State.PUK_REQUIRED) {
+            currentMode = UnlockMode.SimPuk;
         } else {
             final int mode = mLockPatternUtils.getKeyguardStoredPasswordQuality();
             switch (mode) {
index ed5a058..19adb3e 100644 (file)
@@ -55,8 +55,10 @@ public class LockPatternKeyguardViewProperties implements KeyguardViewProperties
 
     private boolean isSimPinSecure() {
         final IccCard.State simState = mUpdateMonitor.getSimState();
-        return (simState == IccCard.State.PIN_REQUIRED || simState == IccCard.State.PUK_REQUIRED
-            || simState == IccCard.State.ABSENT);
+        return (simState == IccCard.State.PIN_REQUIRED
+                || simState == IccCard.State.PUK_REQUIRED
+                || simState == IccCard.State.ABSENT
+                || simState == IccCard.State.PERM_DISABLED);
     }
 
 }
index a9d5ce4..0f187d4 100644 (file)
@@ -126,7 +126,12 @@ class LockScreen extends LinearLayout implements KeyguardScreen,
         /**
          * The sim card is locked.
          */
-        SimLocked(true);
+        SimLocked(true),
+
+        /**
+         * The sim card is permanently disabled due to puk unlock failure
+         */
+        SimPermDisabled(false);
 
         private final boolean mShowStatusLines;
 
@@ -232,7 +237,7 @@ class LockScreen extends LinearLayout implements KeyguardScreen,
 
         /** {@inheritDoc} */
         public void onGrabbedStateChange(View v, int grabbedState) {
-            if (DBG) Log.v(TAG, "*** LockScreen accel is " 
+            if (DBG) Log.v(TAG, "*** LockScreen accel is "
                     + (mEnergyWave.isHardwareAccelerated() ? "on":"off"));
             // Don't poke the wake lock when returning to a state where the handle is
             // not grabbed since that can happen when the system (instead of the user)
@@ -450,7 +455,9 @@ class LockScreen extends LinearLayout implements KeyguardScreen,
      */
     private Status getCurrentStatus(IccCard.State simState) {
         boolean missingAndNotProvisioned = (!mUpdateMonitor.isDeviceProvisioned()
-                && simState == IccCard.State.ABSENT);
+                && (simState == IccCard.State.ABSENT
+                        || simState == IccCard.State.PERM_DISABLED));
+
         if (missingAndNotProvisioned) {
             return Status.SimMissingLocked;
         }
@@ -468,6 +475,8 @@ class LockScreen extends LinearLayout implements KeyguardScreen,
                 return Status.SimPukLocked;
             case READY:
                 return Status.Normal;
+            case PERM_DISABLED:
+                return Status.SimPermDisabled;
             case UNKNOWN:
                 return Status.SimMissing;
         }
@@ -534,11 +543,23 @@ class LockScreen extends LinearLayout implements KeyguardScreen,
             case SimMissing:
                 // text
                 mStatusView.setCarrierText(R.string.lockscreen_missing_sim_message_short);
-                mScreenLocked.setText(R.string.lockscreen_missing_sim_instructions);
+                mScreenLocked.setText(R.string.lockscreen_missing_sim_instructions_long);
 
                 // layout
                 mScreenLocked.setVisibility(View.VISIBLE);
-                mEmergencyCallText.setVisibility(View.VISIBLE);
+                mLockPatternUtils.updateEmergencyCallText(mEmergencyCallText);
+                enableUnlock(); // do not need to show the e-call button; user may unlock
+                break;
+
+            case SimPermDisabled:
+                // text
+                mStatusView.setCarrierText(R.string.lockscreen_missing_sim_message_short);
+                mScreenLocked.setText(
+                        R.string.lockscreen_permanent_disabled_sim_instructions);
+
+                // layout
+                mScreenLocked.setVisibility(View.VISIBLE);
+                mLockPatternUtils.updateEmergencyCallText(mEmergencyCallText);
                 enableUnlock(); // do not need to show the e-call button; user may unlock
                 break;
 
@@ -552,8 +573,8 @@ class LockScreen extends LinearLayout implements KeyguardScreen,
 
                 // layout
                 mScreenLocked.setVisibility(View.VISIBLE);
-                mEmergencyCallText.setVisibility(View.VISIBLE);
-                mEmergencyCallButton.setVisibility(View.VISIBLE);
+                mLockPatternUtils.updateEmergencyCallText(mEmergencyCallText);
+                mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
                 disableUnlock();
                 break;
 
@@ -579,10 +600,15 @@ class LockScreen extends LinearLayout implements KeyguardScreen,
                 mScreenLocked.setText(R.string.lockscreen_sim_puk_locked_instructions);
 
                 // layout
-                mScreenLocked.setVisibility(View.VISIBLE);
-                mEmergencyCallText.setVisibility(View.VISIBLE);
-                mEmergencyCallButton.setVisibility(View.VISIBLE);
-                disableUnlock();
+                mLockPatternUtils.updateEmergencyCallText(mEmergencyCallText);
+                mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
+                if (mLockPatternUtils.isPukUnlockScreenEnable()) {
+                    mScreenLocked.setVisibility(View.INVISIBLE);
+                    enableUnlock();
+                } else {
+                    mScreenLocked.setVisibility(View.VISIBLE);
+                    disableUnlock();
+                }
                 break;
         }
     }
diff --git a/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java b/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java
new file mode 100644 (file)
index 0000000..544bb3d
--- /dev/null
@@ -0,0 +1,470 @@
+/*
+ * 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 com.android.internal.policy.impl;
+
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.widget.LockPatternUtils;
+
+import android.text.Editable;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import com.android.internal.R;
+
+/**
+ * Displays a dialer like interface to unlock the SIM PUK.
+ */
+public class SimPukUnlockScreen extends LinearLayout implements KeyguardScreen,
+    View.OnClickListener, KeyguardUpdateMonitor.InfoCallback {
+
+    private static final int DIGIT_PRESS_WAKE_MILLIS = 5000;
+
+    private final KeyguardUpdateMonitor mUpdateMonitor;
+    private final KeyguardScreenCallback mCallback;
+
+    private TextView mHeaderText;
+    private TextView mPukText;
+    private TextView mPinText;
+
+    private TextView mFocusedEntry;
+
+    private TextView mOkButton;
+    private Button mEmergencyCallButton;
+
+    private View mDelPukButton;
+    private View mDelPinButton;
+
+    private ProgressDialog mSimUnlockProgressDialog = null;
+
+    private LockPatternUtils mLockPatternUtils;
+
+    private int mCreationOrientation;
+
+    private int mKeyboardHidden;
+
+    private static final char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
+
+    public SimPukUnlockScreen(Context context, Configuration configuration,
+            KeyguardUpdateMonitor updateMonitor, KeyguardScreenCallback callback,
+            LockPatternUtils lockpatternutils) {
+        super(context);
+        mUpdateMonitor = updateMonitor;
+        mCallback = callback;;
+
+        mCreationOrientation = configuration.orientation;
+        mKeyboardHidden = configuration.hardKeyboardHidden;
+        mLockPatternUtils = lockpatternutils;
+
+        LayoutInflater inflater = LayoutInflater.from(context);
+        if (mKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
+            inflater.inflate(
+                    R.layout.keyguard_screen_sim_puk_landscape, this, true);
+        } else {
+            inflater.inflate(
+                    R.layout.keyguard_screen_sim_puk_portrait, this, true);
+            new TouchInput();
+        }
+
+        mHeaderText = (TextView) findViewById(R.id.headerText);
+        mPukText = (TextView) findViewById(R.id.pukDisplay);
+        mPukText.setOnClickListener(new OnClickListener() {
+           public void onClick(View v) {
+               requestFocus(mPukText);
+               mCallback.pokeWakelock();
+           }
+        });
+        mPinText = (TextView) findViewById(R.id.pinDisplay);
+        mPinText.setOnClickListener(this);
+
+        mDelPukButton = findViewById(R.id.pukDel);
+        mDelPukButton.setOnClickListener(this);
+        mDelPinButton = findViewById(R.id.pinDel);
+        mDelPinButton.setOnClickListener(this);
+
+
+        mEmergencyCallButton = (Button) findViewById(R.id.emergencyCall);
+        mOkButton = (TextView) findViewById(R.id.ok);
+
+        mHeaderText.setText(R.string.keyguard_password_enter_puk_code);
+        mPukText.setFocusable(false);
+        mPinText.setFocusable(false);
+        mOkButton.setOnClickListener(this);
+
+        requestFocus(mPukText);
+
+        if (mLockPatternUtils.isEmergencyCallCapable()) {
+            mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
+            mEmergencyCallButton.setOnClickListener(this);
+        } else {
+            mEmergencyCallButton.setVisibility(View.GONE);
+        }
+
+        setFocusableInTouchMode(true);
+    }
+
+    private void requestFocus(TextView entry) {
+        mFocusedEntry = entry;
+        mFocusedEntry.setText("");
+    }
+
+    /** {@inheritDoc} */
+    public boolean needsInput() {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    public void onPause() {
+
+    }
+
+    /** {@inheritDoc} */
+    public void onResume() {
+        // start fresh
+        mHeaderText.setText(R.string.keyguard_password_enter_puk_code);
+        requestFocus(mPukText);
+        mPinText.setText("");
+
+        if (mLockPatternUtils.isEmergencyCallCapable()) {
+            mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void cleanUp() {
+        // dismiss the dialog.
+        if (mSimUnlockProgressDialog != null) {
+            mSimUnlockProgressDialog.dismiss();
+            mSimUnlockProgressDialog = null;
+        }
+        mUpdateMonitor.removeCallback(this);
+    }
+
+
+    /**
+     * Since the IPC can block, we want to run the request in a separate thread
+     * with a callback.
+     */
+    private abstract class CheckSimPuk extends Thread {
+
+        private final String mPin, mPuk;
+
+        protected CheckSimPuk(String puk, String pin) {
+            mPuk = puk;
+            mPin = pin;
+        }
+
+        abstract void onSimLockChangedResponse(boolean success);
+
+        @Override
+        public void run() {
+            try {
+                final boolean result = ITelephony.Stub.asInterface(ServiceManager
+                        .checkService("phone")).supplyPuk(mPuk, mPin);
+
+                post(new Runnable() {
+                    public void run() {
+                        onSimLockChangedResponse(result);
+                    }
+                });
+            } catch (RemoteException e) {
+                post(new Runnable() {
+                    public void run() {
+                        onSimLockChangedResponse(false);
+                    }
+                });
+            }
+        }
+    }
+
+    public void onClick(View v) {
+        if (v == mDelPukButton) {
+            final Editable digits = mPukText.getEditableText();
+            final int len = digits.length();
+            if (len > 0) {
+                digits.delete(len-1, len);
+            }
+            mCallback.pokeWakelock();
+        } else if (v == mDelPinButton) {
+            final Editable digits = mPinText.getEditableText();
+            final int len = digits.length();
+            if (len > 0) {
+                digits.delete(len-1, len);
+            }
+            mCallback.pokeWakelock();
+        } else if (v == mPinText) {
+            requestFocus(mPinText);
+            mCallback.pokeWakelock();
+        } else if (v == mEmergencyCallButton) {
+            mCallback.takeEmergencyCallAction();
+        } else if (v == mOkButton) {
+            checkPuk();
+        }
+    }
+
+    private Dialog getSimUnlockProgressDialog() {
+        if (mSimUnlockProgressDialog == null) {
+            mSimUnlockProgressDialog = new ProgressDialog(mContext);
+            mSimUnlockProgressDialog.setMessage(
+                    mContext.getString(R.string.lockscreen_sim_unlock_progress_dialog_message));
+            mSimUnlockProgressDialog.setIndeterminate(true);
+            mSimUnlockProgressDialog.setCancelable(false);
+            mSimUnlockProgressDialog.getWindow().setType(
+                    WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+            if (!mContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_sf_slowBlur)) {
+                mSimUnlockProgressDialog.getWindow().setFlags(
+                        WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
+                        WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
+            }
+        }
+        return mSimUnlockProgressDialog;
+    }
+
+    private void checkPuk() {
+        // make sure that the puk is at least 8 digits long.
+        if (mPukText.getText().length() < 8) {
+            // otherwise, display a message to the user, and don't submit.
+            mHeaderText.setText(R.string.invalidPuk);
+            mPukText.setText("");
+            mCallback.pokeWakelock();
+            return;
+        }
+
+        if (mPinText.getText().length() < 4
+                || mPinText.getText().length() > 8) {
+            // otherwise, display a message to the user, and don't submit.
+            mHeaderText.setText(R.string.invalidPin);
+            mPinText.setText("");
+            mCallback.pokeWakelock();
+            return;
+        }
+
+        getSimUnlockProgressDialog().show();
+
+        new CheckSimPuk(mPukText.getText().toString(),
+                mPinText.getText().toString()) {
+            void onSimLockChangedResponse(boolean success) {
+                if (mSimUnlockProgressDialog != null) {
+                    mSimUnlockProgressDialog.hide();
+                }
+                if (success) {
+                    // before closing the keyguard, report back that
+                    // the sim is unlocked so it knows right away
+                    mUpdateMonitor.reportSimUnlocked();
+                    mCallback.goToUnlockScreen();
+                } else {
+                    mHeaderText.setText(R.string.badPuk);
+                    mPukText.setText("");
+                    mPinText.setText("");
+                }
+                mCallback.pokeWakelock();
+            }
+        }.start();
+    }
+
+
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_BACK) {
+            mCallback.goToLockScreen();
+            return true;
+        }
+        final char match = event.getMatch(DIGITS);
+        if (match != 0) {
+            reportDigit(match - '0');
+            return true;
+        }
+        if (keyCode == KeyEvent.KEYCODE_DEL) {
+            mFocusedEntry.onKeyDown(keyCode, event);
+            final Editable digits = mFocusedEntry.getEditableText();
+            final int len = digits.length();
+            if (len > 0) {
+                digits.delete(len-1, len);
+            }
+            mCallback.pokeWakelock();
+            return true;
+        }
+
+        if (keyCode == KeyEvent.KEYCODE_ENTER) {
+            checkPuk();
+            return true;
+        }
+
+        return false;
+    }
+
+    private void reportDigit(int digit) {
+        mFocusedEntry.append(Integer.toString(digit));
+    }
+
+    void updateConfiguration() {
+        Configuration newConfig = getResources().getConfiguration();
+        if (newConfig.orientation != mCreationOrientation) {
+            mCallback.recreateMe(newConfig);
+        } else if (newConfig.hardKeyboardHidden != mKeyboardHidden) {
+            mKeyboardHidden = newConfig.hardKeyboardHidden;
+            final boolean isKeyboardOpen =
+                (mKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO);
+            if (mUpdateMonitor.isKeyguardBypassEnabled() && isKeyboardOpen) {
+                mCallback.goToUnlockScreen();
+            }
+        }
+
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        updateConfiguration();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        updateConfiguration();
+    }
+
+    /**
+     * Helper class to handle input from touch dialer.  Only relevant when
+     * the keyboard is shut.
+     */
+    private class TouchInput implements View.OnClickListener {
+        private TextView mZero;
+        private TextView mOne;
+        private TextView mTwo;
+        private TextView mThree;
+        private TextView mFour;
+        private TextView mFive;
+        private TextView mSix;
+        private TextView mSeven;
+        private TextView mEight;
+        private TextView mNine;
+        private TextView mCancelButton;
+
+        private TouchInput() {
+            mZero = (TextView) findViewById(R.id.zero);
+            mOne = (TextView) findViewById(R.id.one);
+            mTwo = (TextView) findViewById(R.id.two);
+            mThree = (TextView) findViewById(R.id.three);
+            mFour = (TextView) findViewById(R.id.four);
+            mFive = (TextView) findViewById(R.id.five);
+            mSix = (TextView) findViewById(R.id.six);
+            mSeven = (TextView) findViewById(R.id.seven);
+            mEight = (TextView) findViewById(R.id.eight);
+            mNine = (TextView) findViewById(R.id.nine);
+            mCancelButton = (TextView) findViewById(R.id.cancel);
+
+            mZero.setText("0");
+            mOne.setText("1");
+            mTwo.setText("2");
+            mThree.setText("3");
+            mFour.setText("4");
+            mFive.setText("5");
+            mSix.setText("6");
+            mSeven.setText("7");
+            mEight.setText("8");
+            mNine.setText("9");
+
+            mZero.setOnClickListener(this);
+            mOne.setOnClickListener(this);
+            mTwo.setOnClickListener(this);
+            mThree.setOnClickListener(this);
+            mFour.setOnClickListener(this);
+            mFive.setOnClickListener(this);
+            mSix.setOnClickListener(this);
+            mSeven.setOnClickListener(this);
+            mEight.setOnClickListener(this);
+            mNine.setOnClickListener(this);
+            mCancelButton.setOnClickListener(this);
+        }
+
+
+        public void onClick(View v) {
+            if (v == mCancelButton) {
+                mCallback.goToLockScreen();
+                return;
+            }
+
+            final int digit = checkDigit(v);
+            if (digit >= 0) {
+                mCallback.pokeWakelock(DIGIT_PRESS_WAKE_MILLIS);
+                reportDigit(digit);
+            }
+        }
+
+        private int checkDigit(View v) {
+            int digit = -1;
+            if (v == mZero) {
+                digit = 0;
+            } else if (v == mOne) {
+                digit = 1;
+            } else if (v == mTwo) {
+                digit = 2;
+            } else if (v == mThree) {
+                digit = 3;
+            } else if (v == mFour) {
+                digit = 4;
+            } else if (v == mFive) {
+                digit = 5;
+            } else if (v == mSix) {
+                digit = 6;
+            } else if (v == mSeven) {
+                digit = 7;
+            } else if (v == mEight) {
+                digit = 8;
+            } else if (v == mNine) {
+                digit = 9;
+            }
+            return digit;
+        }
+    }
+
+    public void onPhoneStateChanged(String newState) {
+        if (mLockPatternUtils.isEmergencyCallCapable()) {
+            mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
+        }
+    }
+
+    public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
+
+    }
+
+    public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
+
+    }
+
+    public void onRingerModeChanged(int state) {
+
+    }
+
+    public void onTimeChanged() {
+
+    }
+}
index 486e7aa..7255c27 100644 (file)
@@ -92,16 +92,17 @@ public class SimUnlockScreen extends LinearLayout implements KeyguardScreen, Vie
         mBackSpaceButton = findViewById(R.id.backspace);
         mBackSpaceButton.setOnClickListener(this);
 
-        mEmergencyCallButton = (Button) findViewById(R.id.emergencyCall);
-        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
         mOkButton = (TextView) findViewById(R.id.ok);
 
         mHeaderText.setText(R.string.keyguard_password_enter_pin_code);
         mPinText.setFocusable(false);
 
-        mEmergencyCallButton.setOnClickListener(this);
         mOkButton.setOnClickListener(this);
 
+        mEmergencyCallButton = (Button) findViewById(R.id.emergencyCall);
+        mEmergencyCallButton.setOnClickListener(this);
+        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
+
         setFocusableInTouchMode(true);
     }
 
@@ -229,7 +230,7 @@ public class SimUnlockScreen extends LinearLayout implements KeyguardScreen, Vie
                 if (success) {
                     // before closing the keyguard, report back that
                     // the sim is unlocked so it knows right away
-                    mUpdateMonitor.reportSimPinUnlocked();
+                    mUpdateMonitor.reportSimUnlocked();
                     mCallback.goToUnlockScreen();
                 } else {
                     mHeaderText.setText(R.string.keyguard_password_wrong_pin_code);
@@ -291,9 +292,8 @@ public class SimUnlockScreen extends LinearLayout implements KeyguardScreen, Vie
                 mCallback.goToUnlockScreen();
             }
         }
-        
     }
-    
+
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
@@ -403,7 +403,7 @@ public class SimUnlockScreen extends LinearLayout implements KeyguardScreen, Vie
     }
 
     public void onPhoneStateChanged(String newState) {
-        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
+            mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
     }
 
     public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
index b1552a8..42be756 100644 (file)
@@ -26,19 +26,23 @@ import android.net.ConnectivityManager;
 import android.net.DummyDataStateTracker;
 import android.net.EthernetDataTracker;
 import android.net.IConnectivityManager;
+import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.MobileDataStateTracker;
+import android.net.NetworkConfig;
 import android.net.NetworkInfo;
 import android.net.NetworkStateTracker;
 import android.net.NetworkUtils;
 import android.net.Proxy;
 import android.net.ProxyProperties;
+import android.net.RouteInfo;
 import android.net.vpn.VpnManager;
 import android.net.wifi.WifiStateTracker;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
@@ -58,6 +62,7 @@ import java.io.FileWriter;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.net.InetAddress;
+import java.net.Inet4Address;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -79,6 +84,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
     private static final String NETWORK_RESTORE_DELAY_PROP_NAME =
             "android.telephony.apn-restore";
 
+    // used in recursive route setting to add gateways for the host for which
+    // a host route was requested.
+    private static final int MAX_HOSTROUTE_CYCLE_COUNT = 10;
+
     private Tethering mTethering;
     private boolean mTetheringConfigValid = false;
 
@@ -118,6 +127,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
 
     private AtomicBoolean mBackgroundDataEnabled = new AtomicBoolean(true);
 
+    private INetworkManagementService mNetd;
+
     private static final int ENABLED  = 1;
     private static final int DISABLED = 0;
 
@@ -189,6 +200,14 @@ public class ConnectivityService extends IConnectivityManager.Stub {
     private static final int EVENT_APPLY_GLOBAL_HTTP_PROXY =
             MAX_NETWORK_STATE_TRACKER_EVENT + 9;
 
+    /**
+     * used internally to set external dependency met/unmet
+     * arg1 = ENABLED (met) or DISABLED (unmet)
+     * arg2 = NetworkType
+     */
+    private static final int EVENT_SET_DEPENDENCY_MET =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 10;
+
     private Handler mHandler;
 
     // list of DeathRecipients used to make sure features are turned off when
@@ -217,28 +236,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
 
     private SettingsObserver mSettingsObserver;
 
-    private static class NetworkAttributes {
-        /**
-         * Class for holding settings read from resources.
-         */
-        public String mName;
-        public int mType;
-        public int mRadio;
-        public int mPriority;
-        public NetworkInfo.State mLastState;
-        public NetworkAttributes(String init) {
-            String fragments[] = init.split(",");
-            mName = fragments[0].toLowerCase();
-            mType = Integer.parseInt(fragments[1]);
-            mRadio = Integer.parseInt(fragments[2]);
-            mPriority = Integer.parseInt(fragments[3]);
-            mLastState = NetworkInfo.State.UNKNOWN;
-        }
-        public boolean isDefault() {
-            return (mType == mRadio);
-        }
-    }
-    NetworkAttributes[] mNetAttributes;
+    NetworkConfig[] mNetConfigs;
     int mNetworksDefined;
 
     private static class RadioAttributes {
@@ -252,6 +250,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
     }
     RadioAttributes[] mRadioAttributes;
 
+    // the set of network types that can only be enabled by system/sig apps
+    List mProtectedNetworks;
+
     public static synchronized ConnectivityService getInstance(Context context) {
         if (sServiceInstance == null) {
             sServiceInstance = new ConnectivityService(context);
@@ -305,7 +306,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
         mNetworkPreference = getPersistedNetworkPreference();
 
         mRadioAttributes = new RadioAttributes[ConnectivityManager.MAX_RADIO_TYPE+1];
-        mNetAttributes = new NetworkAttributes[ConnectivityManager.MAX_NETWORK_TYPE+1];
+        mNetConfigs = new NetworkConfig[ConnectivityManager.MAX_NETWORK_TYPE+1];
 
         // Load device network attributes from resources
         String[] raStrings = context.getResources().getStringArray(
@@ -328,29 +329,40 @@ public class ConnectivityService extends IConnectivityManager.Stub {
                 com.android.internal.R.array.networkAttributes);
         for (String naString : naStrings) {
             try {
-                NetworkAttributes n = new NetworkAttributes(naString);
-                if (n.mType > ConnectivityManager.MAX_NETWORK_TYPE) {
+                NetworkConfig n = new NetworkConfig(naString);
+                if (n.type > ConnectivityManager.MAX_NETWORK_TYPE) {
                     loge("Error in networkAttributes - ignoring attempt to define type " +
-                            n.mType);
+                            n.type);
                     continue;
                 }
-                if (mNetAttributes[n.mType] != null) {
+                if (mNetConfigs[n.type] != null) {
                     loge("Error in networkAttributes - ignoring attempt to redefine type " +
-                            n.mType);
+                            n.type);
                     continue;
                 }
-                if (mRadioAttributes[n.mRadio] == null) {
+                if (mRadioAttributes[n.radio] == null) {
                     loge("Error in networkAttributes - ignoring attempt to use undefined " +
-                            "radio " + n.mRadio + " in network type " + n.mType);
+                            "radio " + n.radio + " in network type " + n.type);
                     continue;
                 }
-                mNetAttributes[n.mType] = n;
+                mNetConfigs[n.type] = n;
                 mNetworksDefined++;
             } catch(Exception e) {
                 // ignore it - leave the entry null
             }
         }
 
+        mProtectedNetworks = new ArrayList<Integer>();
+        int[] protectedNetworks = context.getResources().getIntArray(
+                com.android.internal.R.array.config_protectedNetworks);
+        for (int p : protectedNetworks) {
+            if ((mNetConfigs[p] != null) && (mProtectedNetworks.contains(p) == false)) {
+                mProtectedNetworks.add(p);
+            } else {
+                if (DBG) loge("Ignoring protectedNetwork " + p);
+            }
+        }
+
         // high priority first
         mPriorityList = new int[mNetworksDefined];
         {
@@ -358,16 +370,16 @@ public class ConnectivityService extends IConnectivityManager.Stub {
             int currentLowest = 0;
             int nextLowest = 0;
             while (insertionPoint > -1) {
-                for (NetworkAttributes na : mNetAttributes) {
+                for (NetworkConfig na : mNetConfigs) {
                     if (na == null) continue;
-                    if (na.mPriority < currentLowest) continue;
-                    if (na.mPriority > currentLowest) {
-                        if (na.mPriority < nextLowest || nextLowest == 0) {
-                            nextLowest = na.mPriority;
+                    if (na.priority < currentLowest) continue;
+                    if (na.priority > currentLowest) {
+                        if (na.priority < nextLowest || nextLowest == 0) {
+                            nextLowest = na.priority;
                         }
                         continue;
                     }
-                    mPriorityList[insertionPoint--] = na.mType;
+                    mPriorityList[insertionPoint--] = na.type;
                 }
                 currentLowest = nextLowest;
                 nextLowest = 0;
@@ -393,7 +405,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
          * to change very often.
          */
         for (int netType : mPriorityList) {
-            switch (mNetAttributes[netType].mRadio) {
+            switch (mNetConfigs[netType].radio) {
             case ConnectivityManager.TYPE_WIFI:
                 if (DBG) log("Starting Wifi Service.");
                 WifiStateTracker wst = new WifiStateTracker();
@@ -409,12 +421,12 @@ public class ConnectivityService extends IConnectivityManager.Stub {
                 break;
             case ConnectivityManager.TYPE_MOBILE:
                 mNetTrackers[netType] = new MobileDataStateTracker(netType,
-                        mNetAttributes[netType].mName);
+                        mNetConfigs[netType].name);
                 mNetTrackers[netType].startMonitoring(context, mHandler);
                 break;
             case ConnectivityManager.TYPE_DUMMY:
                 mNetTrackers[netType] = new DummyDataStateTracker(netType,
-                        mNetAttributes[netType].mName);
+                        mNetConfigs[netType].name);
                 mNetTrackers[netType].startMonitoring(context, mHandler);
                 break;
             case ConnectivityManager.TYPE_BLUETOOTH:
@@ -427,18 +439,16 @@ public class ConnectivityService extends IConnectivityManager.Stub {
                 break;
             default:
                 loge("Trying to create a DataStateTracker for an unknown radio type " +
-                        mNetAttributes[netType].mRadio);
+                        mNetConfigs[netType].radio);
                 continue;
             }
         }
 
         mTethering = new Tethering(mContext, mHandler.getLooper());
-        mTetheringConfigValid = (((mNetTrackers[ConnectivityManager.TYPE_MOBILE_DUN] != null) ||
-                                  !mTethering.isDunRequired()) &&
-                                 (mTethering.getTetherableUsbRegexs().length != 0 ||
+        mTetheringConfigValid = ((mTethering.getTetherableUsbRegexs().length != 0 ||
                                   mTethering.getTetherableWifiRegexs().length != 0 ||
                                   mTethering.getTetherableBluetoothRegexs().length != 0) &&
-                                 mTethering.getUpstreamIfaceRegexs().length != 0);
+                                 mTethering.getUpstreamIfaceTypes().length != 0);
 
         if (DBG) {
             mInetLog = new ArrayList();
@@ -474,8 +484,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
 
     private void handleSetNetworkPreference(int preference) {
         if (ConnectivityManager.isNetworkTypeValid(preference) &&
-                mNetAttributes[preference] != null &&
-                mNetAttributes[preference].isDefault()) {
+                mNetConfigs[preference] != null &&
+                mNetConfigs[preference].isDefault()) {
             if (mNetworkPreference != preference) {
                 final ContentResolver cr = mContext.getContentResolver();
                 Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE, preference);
@@ -542,21 +552,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
      * active
      */
     public NetworkInfo getActiveNetworkInfo() {
-        enforceAccessPermission();
-        for (int type=0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
-            if (mNetAttributes[type] == null || !mNetAttributes[type].isDefault()) {
-                continue;
-            }
-            NetworkStateTracker t = mNetTrackers[type];
-            NetworkInfo info = t.getNetworkInfo();
-            if (info.isConnected()) {
-                if (DBG && type != mActiveDefaultNetwork) {
-                    loge("connected default network is not mActiveDefaultNetwork!");
-                }
-                return info;
-            }
-        }
-        return null;
+        return getNetworkInfo(mActiveDefaultNetwork);
     }
 
     public NetworkInfo getNetworkInfo(int networkType) {
@@ -588,18 +584,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
      * none is active
      */
     public LinkProperties getActiveLinkProperties() {
-        enforceAccessPermission();
-        for (int type=0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
-            if (mNetAttributes[type] == null || !mNetAttributes[type].isDefault()) {
-                continue;
-            }
-            NetworkStateTracker t = mNetTrackers[type];
-            NetworkInfo info = t.getNetworkInfo();
-            if (info.isConnected()) {
-                return t.getLinkProperties();
-            }
-        }
-        return null;
+        return getLinkProperties(mActiveDefaultNetwork);
     }
 
     public LinkProperties getLinkProperties(int networkType) {
@@ -692,7 +677,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
         }
         enforceChangePermission();
         if (!ConnectivityManager.isNetworkTypeValid(networkType) ||
-                mNetAttributes[networkType] == null) {
+                mNetConfigs[networkType] == null) {
             return Phone.APN_REQUEST_FAILED;
         }
 
@@ -701,17 +686,17 @@ public class ConnectivityService extends IConnectivityManager.Stub {
         // TODO - move this into the MobileDataStateTracker
         int usedNetworkType = networkType;
         if(networkType == ConnectivityManager.TYPE_MOBILE) {
-            if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
-                usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS;
-            } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
-                usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL;
-            } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN) ||
-                    TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN_ALWAYS)) {
-                usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN;
-            } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) {
-                usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI;
+            usedNetworkType = convertFeatureToNetworkType(feature);
+            if (usedNetworkType < 0) {
+                Slog.e(TAG, "Can't match any netTracker!");
+                usedNetworkType = networkType;
             }
         }
+
+        if (mProtectedNetworks.contains(usedNetworkType)) {
+            enforceConnectivityInternalPermission();
+        }
+
         NetworkStateTracker network = mNetTrackers[usedNetworkType];
         if (network != null) {
             Integer currentPid = new Integer(getCallingPid());
@@ -735,9 +720,13 @@ public class ConnectivityService extends IConnectivityManager.Stub {
                         mNetRequestersPids[usedNetworkType].add(currentPid);
                     }
                 }
-                mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_RESTORE_DEFAULT_NETWORK,
-                        f), getRestoreDefaultNetworkDelay());
 
+                int restoreTimer = getRestoreDefaultNetworkDelay(usedNetworkType);
+
+                if (restoreTimer >= 0) {
+                    mHandler.sendMessageDelayed(
+                            mHandler.obtainMessage(EVENT_RESTORE_DEFAULT_NETWORK, f), restoreTimer);
+                }
 
                 if ((ni.isConnectedOrConnecting() == true) &&
                         !network.isTeardownRequested()) {
@@ -853,15 +842,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
             // TODO - move to MobileDataStateTracker
             int usedNetworkType = networkType;
             if (networkType == ConnectivityManager.TYPE_MOBILE) {
-                if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
-                    usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS;
-                } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
-                    usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL;
-                } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN) ||
-                        TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN_ALWAYS)) {
-                    usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN;
-                } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) {
-                    usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI;
+                usedNetworkType = convertFeatureToNetworkType(feature);
+                if (usedNetworkType < 0) {
+                    usedNetworkType = networkType;
                 }
             }
             tracker =  mNetTrackers[usedNetworkType];
@@ -924,6 +907,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
      */
     public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) {
         enforceChangePermission();
+        if (mProtectedNetworks.contains(networkType)) {
+            enforceConnectivityInternalPermission();
+        }
+
         if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
             return false;
         }
@@ -939,7 +926,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
         }
         try {
             InetAddress addr = InetAddress.getByAddress(hostAddress);
-            return addHostRoute(tracker, addr);
+            return addHostRoute(tracker, addr, 0);
         } catch (UnknownHostException e) {}
         return false;
     }
@@ -952,24 +939,49 @@ public class ConnectivityService extends IConnectivityManager.Stub {
      * TODO - deprecate
      * @return {@code true} on success, {@code false} on failure
      */
-    private boolean addHostRoute(NetworkStateTracker nt, InetAddress hostAddress) {
-        if (nt.getNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI) {
-            return false;
-        }
-
-        LinkProperties p = nt.getLinkProperties();
-        if (p == null) return false;
-        String interfaceName = p.getInterfaceName();
+    private boolean addHostRoute(NetworkStateTracker nt, InetAddress hostAddress, int cycleCount) {
+        LinkProperties lp = nt.getLinkProperties();
+        if ((lp == null) || (hostAddress == null)) return false;
 
+        String interfaceName = lp.getInterfaceName();
         if (DBG) {
-            log("Requested host route to " + hostAddress + "(" + interfaceName + ")");
+            log("Requested host route to " + hostAddress + "(" + interfaceName + "), cycleCount=" +
+                    cycleCount);
         }
-        if (interfaceName != null) {
-            return NetworkUtils.addHostRoute(interfaceName, hostAddress, null);
-        } else {
+        if (interfaceName == null) {
             if (DBG) loge("addHostRoute failed due to null interface name");
             return false;
         }
+
+        RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getRoutes(), hostAddress);
+        InetAddress gatewayAddress = null;
+        if (bestRoute != null) {
+            gatewayAddress = bestRoute.getGateway();
+            // if the best route is ourself, don't relf-reference, just add the host route
+            if (hostAddress.equals(gatewayAddress)) gatewayAddress = null;
+        }
+        if (gatewayAddress != null) {
+            if (cycleCount > MAX_HOSTROUTE_CYCLE_COUNT) {
+                loge("Error adding hostroute - too much recursion");
+                return false;
+            }
+            if (!addHostRoute(nt, gatewayAddress, cycleCount+1)) return false;
+        }
+
+        RouteInfo route = RouteInfo.makeHostRoute(hostAddress, gatewayAddress);
+
+        try {
+            mNetd.addRoute(interfaceName, route);
+            return true;
+        } catch (Exception ex) {
+            return false;
+        }
+    }
+
+    // TODO support the removal of single host routes.  Keep a ref count of them so we
+    // aren't over-zealous
+    private boolean removeHostRoute(NetworkStateTracker nt, InetAddress hostAddress) {
+        return false;
     }
 
     /**
@@ -1015,6 +1027,25 @@ public class ConnectivityService extends IConnectivityManager.Stub {
         return retVal;
     }
 
+    public void setDataDependency(int networkType, boolean met) {
+        enforceConnectivityInternalPermission();
+
+        if (DBG) {
+            log("setDataDependency(" + networkType + ", " + met + ")");
+        }
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_DEPENDENCY_MET,
+                (met ? ENABLED : DISABLED), networkType));
+    }
+
+    private void handleSetDependencyMet(int networkType, boolean met) {
+        if (mNetTrackers[networkType] != null) {
+            if (DBG) {
+                log("handleSetDependencyMet(" + networkType + ", " + met + ")");
+            }
+            mNetTrackers[networkType].setDependencyMet(met);
+        }
+    }
+
     /**
      * @see ConnectivityManager#setMobileDataEnabled(boolean)
      */
@@ -1023,7 +1054,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
         if (DBG) log("setMobileDataEnabled(" + enabled + ")");
 
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_MOBILE_DATA,
-            (enabled ? ENABLED : DISABLED), 0));
+                (enabled ? ENABLED : DISABLED), 0));
     }
 
     private void handleSetMobileData(boolean enabled) {
@@ -1084,7 +1115,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
          * getting the disconnect for a network that we explicitly disabled
          * in accordance with network preference policies.
          */
-        if (!mNetAttributes[prevNetType].isDefault()) {
+        if (!mNetConfigs[prevNetType].isDefault()) {
             List pids = mNetRequestersPids[prevNetType];
             for (int i = 0; i<pids.size(); i++) {
                 Integer pid = (Integer)pids.get(i);
@@ -1109,7 +1140,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
                     info.getExtraInfo());
         }
 
-        if (mNetAttributes[prevNetType].isDefault()) {
+        if (mNetConfigs[prevNetType].isDefault()) {
             tryFailover(prevNetType);
             if (mActiveDefaultNetwork != -1) {
                 NetworkInfo switchTo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
@@ -1120,8 +1151,30 @@ public class ConnectivityService extends IConnectivityManager.Stub {
             }
         }
         intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION, mDefaultInetConditionPublished);
+
+        // Reset interface if no other connections are using the same interface
+        boolean doReset = true;
+        LinkProperties linkProperties = mNetTrackers[prevNetType].getLinkProperties();
+        if (linkProperties != null) {
+            String oldIface = linkProperties.getInterfaceName();
+            if (TextUtils.isEmpty(oldIface) == false) {
+                for (NetworkStateTracker networkStateTracker : mNetTrackers) {
+                    if (networkStateTracker == null) continue;
+                    NetworkInfo networkInfo = networkStateTracker.getNetworkInfo();
+                    if (networkInfo.isConnected() && networkInfo.getType() != prevNetType) {
+                        LinkProperties l = networkStateTracker.getLinkProperties();
+                        if (l == null) continue;
+                        if (oldIface.equals(l.getInterfaceName())) {
+                            doReset = false;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
         // do this before we broadcast the change
-        handleConnectivityChange(prevNetType);
+        handleConnectivityChange(prevNetType, doReset);
 
         sendStickyBroadcast(intent);
         /*
@@ -1139,7 +1192,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
          * Try to reconnect on all available and let them hash it out when
          * more than one connects.
          */
-        if (mNetAttributes[prevNetType].isDefault()) {
+        if (mNetConfigs[prevNetType].isDefault()) {
             if (mActiveDefaultNetwork == prevNetType) {
                 mActiveDefaultNetwork = -1;
             }
@@ -1149,12 +1202,12 @@ public class ConnectivityService extends IConnectivityManager.Stub {
             // TODO - don't filter by priority now - nice optimization but risky
 //            int currentPriority = -1;
 //            if (mActiveDefaultNetwork != -1) {
-//                currentPriority = mNetAttributes[mActiveDefaultNetwork].mPriority;
+//                currentPriority = mNetConfigs[mActiveDefaultNetwork].mPriority;
 //            }
             for (int checkType=0; checkType <= ConnectivityManager.MAX_NETWORK_TYPE; checkType++) {
                 if (checkType == prevNetType) continue;
-                if (mNetAttributes[checkType] == null) continue;
-                if (!mNetAttributes[checkType].isDefault()) continue;
+                if (mNetConfigs[checkType] == null) continue;
+                if (!mNetConfigs[checkType].isDefault()) continue;
 
 // Enabling the isAvailable() optimization caused mobile to not get
 // selected if it was in the middle of error handling. Specifically
@@ -1166,7 +1219,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
 // complete before it is really complete.
 //                if (!mNetTrackers[checkType].isAvailable()) continue;
 
-//                if (currentPriority >= mNetAttributes[checkType].mPriority) continue;
+//                if (currentPriority >= mNetConfigs[checkType].mPriority) continue;
 
                 NetworkStateTracker checkTracker = mNetTrackers[checkType];
                 NetworkInfo checkInfo = checkTracker.getNetworkInfo();
@@ -1239,7 +1292,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
             info.setFailover(false);
         }
 
-        if (mNetAttributes[info.getType()].isDefault()) {
+        if (mNetConfigs[info.getType()].isDefault()) {
             tryFailover(info.getType());
             if (mActiveDefaultNetwork != -1) {
                 NetworkInfo switchTo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
@@ -1272,6 +1325,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
     }
 
     void systemReady() {
+        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+        mNetd = INetworkManagementService.Stub.asInterface(b);
+
         synchronized(this) {
             mSystemReady = true;
             if (mInitialBroadcast != null) {
@@ -1292,11 +1348,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
 
         // if this is a default net and other default is running
         // kill the one not preferred
-        if (mNetAttributes[type].isDefault()) {
+        if (mNetConfigs[type].isDefault()) {
             if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != type) {
                 if ((type != mNetworkPreference &&
-                        mNetAttributes[mActiveDefaultNetwork].mPriority >
-                        mNetAttributes[type].mPriority) ||
+                        mNetConfigs[mActiveDefaultNetwork].priority >
+                        mNetConfigs[type].priority) ||
                         mNetworkPreference == mActiveDefaultNetwork) {
                         // don't accept this one
                         if (DBG) {
@@ -1342,7 +1398,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
         }
         thisNet.setTeardownRequested(false);
         updateNetworkSettings(thisNet);
-        handleConnectivityChange(type);
+        handleConnectivityChange(type, false);
         sendConnectedBroadcast(info);
     }
 
@@ -1352,7 +1408,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
      * according to which networks are connected, and ensuring that the
      * right routing table entries exist.
      */
-    private void handleConnectivityChange(int netType) {
+    private void handleConnectivityChange(int netType, boolean doReset) {
         /*
          * If a non-default network is enabled, add the host routes that
          * will allow it's DNS servers to be accessed.
@@ -1360,19 +1416,42 @@ public class ConnectivityService extends IConnectivityManager.Stub {
         handleDnsConfigurationChange(netType);
 
         if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
-            if (mNetAttributes[netType].isDefault()) {
+            if (mNetConfigs[netType].isDefault()) {
                 handleApplyDefaultProxy(netType);
                 addDefaultRoute(mNetTrackers[netType]);
             } else {
                 addPrivateDnsRoutes(mNetTrackers[netType]);
             }
         } else {
-            if (mNetAttributes[netType].isDefault()) {
+            if (mNetConfigs[netType].isDefault()) {
                 removeDefaultRoute(mNetTrackers[netType]);
             } else {
                 removePrivateDnsRoutes(mNetTrackers[netType]);
             }
         }
+
+        if (doReset) {
+            LinkProperties linkProperties = mNetTrackers[netType].getLinkProperties();
+            if (linkProperties != null) {
+                String iface = linkProperties.getInterfaceName();
+                if (TextUtils.isEmpty(iface) == false) {
+                    if (DBG) {
+                        log("resetConnections(" + iface + ", NetworkUtils.RESET_ALL_ADDRESSES)");
+                    }
+                    NetworkUtils.resetConnections(iface, NetworkUtils.RESET_ALL_ADDRESSES);
+                }
+            }
+        }
+
+        // TODO: Temporary notifying upstread change to Tethering.
+        //       @see bug/4455071
+        /** Notify TetheringService if interface name has been changed. */
+        if (TextUtils.equals(mNetTrackers[netType].getNetworkInfo().getReason(),
+                             Phone.REASON_LINK_PROPERTIES_CHANGED)) {
+            if (isTetheringSupported()) {
+                mTethering.handleTetherIfaceChange();
+            }
+        }
     }
 
     private void addPrivateDnsRoutes(NetworkStateTracker nt) {
@@ -1388,16 +1467,13 @@ public class ConnectivityService extends IConnectivityManager.Stub {
         if (interfaceName != null && !privateDnsRouteSet) {
             Collection<InetAddress> dnsList = p.getDnses();
             for (InetAddress dns : dnsList) {
-                if (DBG) log("  adding " + dns);
-                NetworkUtils.addHostRoute(interfaceName, dns, null);
+                addHostRoute(nt, dns, 0);
             }
             nt.privateDnsRouteSet(true);
         }
     }
 
     private void removePrivateDnsRoutes(NetworkStateTracker nt) {
-        // TODO - we should do this explicitly but the NetUtils api doesnt
-        // support this yet - must remove all.  No worse than before
         LinkProperties p = nt.getLinkProperties();
         if (p == null) return;
         String interfaceName = p.getInterfaceName();
@@ -1407,7 +1483,17 @@ public class ConnectivityService extends IConnectivityManager.Stub {
                 log("removePrivateDnsRoutes for " + nt.getNetworkInfo().getTypeName() +
                         " (" + interfaceName + ")");
             }
-            NetworkUtils.removeHostRoutes(interfaceName);
+
+            Collection<InetAddress> dnsList = p.getDnses();
+            for (InetAddress dns : dnsList) {
+                if (DBG) log("  removing " + dns);
+                RouteInfo route = RouteInfo.makeHostRoute(dns);
+                try {
+                    mNetd.removeRoute(interfaceName, route);
+                } catch (Exception ex) {
+                    loge("error (" + ex + ") removing dns route " + route);
+                }
+            }
             nt.privateDnsRouteSet(false);
         }
     }
@@ -1418,14 +1504,27 @@ public class ConnectivityService extends IConnectivityManager.Stub {
         if (p == null) return;
         String interfaceName = p.getInterfaceName();
         if (TextUtils.isEmpty(interfaceName)) return;
-        for (InetAddress gateway : p.getGateways()) {
 
-            if (NetworkUtils.addHostRoute(interfaceName, gateway, null) &&
-                    NetworkUtils.addDefaultRoute(interfaceName, gateway)) {
-                if (DBG) {
-                    NetworkInfo networkInfo = nt.getNetworkInfo();
-                    log("addDefaultRoute for " + networkInfo.getTypeName() +
-                            " (" + interfaceName + "), GatewayAddr=" + gateway.getHostAddress());
+        for (RouteInfo route : p.getRoutes()) {
+            //TODO - handle non-default routes
+            if (route.isDefaultRoute()) {
+                if (DBG) log("adding default route " + route);
+                InetAddress gateway = route.getGateway();
+                if (addHostRoute(nt, gateway, 0)) {
+                    try {
+                        mNetd.addRoute(interfaceName, route);
+                    } catch (Exception e) {
+                        loge("error adding default route " + route);
+                        continue;
+                    }
+                    if (DBG) {
+                        NetworkInfo networkInfo = nt.getNetworkInfo();
+                        log("addDefaultRoute for " + networkInfo.getTypeName() +
+                                " (" + interfaceName + "), GatewayAddr=" +
+                                gateway.getHostAddress());
+                    }
+                } else {
+                    loge("error adding host route for default route " + route);
                 }
             }
         }
@@ -1437,8 +1536,17 @@ public class ConnectivityService extends IConnectivityManager.Stub {
         if (p == null) return;
         String interfaceName = p.getInterfaceName();
 
-        if (interfaceName != null) {
-            if (NetworkUtils.removeDefaultRoute(interfaceName) >= 0) {
+        if (interfaceName == null) return;
+
+        for (RouteInfo route : p.getRoutes()) {
+            //TODO - handle non-default routes
+            if (route.isDefaultRoute()) {
+                try {
+                    mNetd.removeRoute(interfaceName, route);
+                } catch (Exception ex) {
+                    loge("error (" + ex + ") removing default route " + route);
+                    continue;
+                }
                 if (DBG) {
                     NetworkInfo networkInfo = nt.getNetworkInfo();
                     log("removeDefaultRoute for " + networkInfo.getTypeName() + " (" +
@@ -1528,7 +1636,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
     {
         if (DBG) log("reassessPidDns for pid " + myPid);
         for(int i : mPriorityList) {
-            if (mNetAttributes[i].isDefault()) {
+            if (mNetConfigs[i].isDefault()) {
                 continue;
             }
             NetworkStateTracker nt = mNetTrackers[i];
@@ -1610,7 +1718,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
             if (p == null) return;
             Collection<InetAddress> dnses = p.getDnses();
             boolean changed = false;
-            if (mNetAttributes[netType].isDefault()) {
+            if (mNetConfigs[netType].isDefault()) {
                 int j = 1;
                 if (dnses.size() == 0 && mDefaultDns != null) {
                     String dnsString = mDefaultDns.getHostAddress();
@@ -1657,7 +1765,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
         }
     }
 
-    private int getRestoreDefaultNetworkDelay() {
+    private int getRestoreDefaultNetworkDelay(int networkType) {
         String restoreDefaultNetworkDelayStr = SystemProperties.get(
                 NETWORK_RESTORE_DELAY_PROP_NAME);
         if(restoreDefaultNetworkDelayStr != null &&
@@ -1667,7 +1775,14 @@ public class ConnectivityService extends IConnectivityManager.Stub {
             } catch (NumberFormatException e) {
             }
         }
-        return RESTORE_DEFAULT_NETWORK_DELAY;
+        // if the system property isn't set, use the value for the apn type
+        int ret = RESTORE_DEFAULT_NETWORK_DELAY;
+
+        if ((networkType <= ConnectivityManager.MAX_NETWORK_TYPE) &&
+                (mNetConfigs[networkType] != null)) {
+            ret = mNetConfigs[networkType].restoreTime;
+        }
+        return ret;
     }
 
     @Override
@@ -1741,23 +1856,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
                     info = (NetworkInfo) msg.obj;
                     int type = info.getType();
                     NetworkInfo.State state = info.getState();
-                    // only do this optimization for wifi.  It going into scan mode for location
-                    // services generates alot of noise.  Meanwhile the mms apn won't send out
-                    // subsequent notifications when on default cellular because it never
-                    // disconnects..  so only do this to wifi notifications.  Fixed better when the
-                    // APN notifications are standardized.
-                    if (mNetAttributes[type].mLastState == state &&
-                            mNetAttributes[type].mRadio == ConnectivityManager.TYPE_WIFI) {
-                        if (DBG) {
-                            // TODO - remove this after we validate the dropping doesn't break
-                            // anything
-                            log("Dropping ConnectivityChange for " +
-                                    info.getTypeName() + ": " +
-                                    state + "/" + info.getDetailedState());
-                        }
-                        return;
-                    }
-                    mNetAttributes[type].mLastState = state;
 
                     if (DBG) log("ConnectivityChange for " +
                             info.getTypeName() + ": " +
@@ -1796,8 +1894,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
                     break;
                 case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
                     info = (NetworkInfo) msg.obj;
-                    type = info.getType();
-                    handleConnectivityChange(type);
+                    // TODO: Temporary allowing network configuration
+                    //       change not resetting sockets.
+                    //       @see bug/4455071
+                    handleConnectivityChange(info.getType(), false);
                     break;
                 case EVENT_CLEAR_NET_TRANSITION_WAKELOCK:
                     String causedBy = null;
@@ -1851,6 +1951,13 @@ public class ConnectivityService extends IConnectivityManager.Stub {
                 case EVENT_APPLY_GLOBAL_HTTP_PROXY:
                 {
                     handleDeprecatedGlobalHttpProxy();
+                    break;
+                }
+                case EVENT_SET_DEPENDENCY_MET:
+                {
+                    boolean met = (msg.arg1 == ENABLED);
+                    handleSetDependencyMet(msg.arg2, met);
+                    break;
                 }
             }
         }
@@ -2184,4 +2291,24 @@ public class ConnectivityService extends IConnectivityManager.Stub {
     private void loge(String s) {
         Slog.e(TAG, s);
     }
+    int convertFeatureToNetworkType(String feature){
+        int networkType = -1;
+        if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
+            networkType = ConnectivityManager.TYPE_MOBILE_MMS;
+        } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
+            networkType = ConnectivityManager.TYPE_MOBILE_SUPL;
+        } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN) ||
+                TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN_ALWAYS)) {
+            networkType = ConnectivityManager.TYPE_MOBILE_DUN;
+        } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) {
+            networkType = ConnectivityManager.TYPE_MOBILE_HIPRI;
+        } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_FOTA)) {
+            networkType = ConnectivityManager.TYPE_MOBILE_FOTA;
+        } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_IMS)) {
+            networkType = ConnectivityManager.TYPE_MOBILE_IMS;
+        } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_CBS)) {
+            networkType = ConnectivityManager.TYPE_MOBILE_CBS;
+        }
+        return networkType;
+    }
 }
index 44f5df2..0b4b958 100644 (file)
@@ -28,6 +28,7 @@ import android.net.InterfaceConfiguration;
 import android.net.INetworkManagementEventObserver;
 import android.net.LinkAddress;
 import android.net.NetworkUtils;
+import android.net.RouteInfo;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.os.INetworkManagementService;
@@ -43,11 +44,16 @@ import android.provider.Settings;
 import android.content.ContentResolver;
 import android.database.ContentObserver;
 
+import java.io.BufferedReader;
+import java.io.DataInputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileReader;
+import java.io.InputStreamReader;
+import java.io.IOException;
 import java.lang.IllegalStateException;
-
 import java.net.InetAddress;
+import java.net.Inet4Address;
 import java.net.UnknownHostException;
 import java.util.concurrent.CountDownLatch;
 
@@ -60,6 +66,9 @@ class NetworkManagementService extends INetworkManagementService.Stub {
     private static final boolean DBG = false;
     private static final String NETD_TAG = "NetdConnector";
 
+    private static final int ADD = 1;
+    private static final int REMOVE = 2;
+
     class NetdResponseCode {
         public static final int InterfaceListResult       = 110;
         public static final int TetherInterfaceListResult = 111;
@@ -309,6 +318,164 @@ class NetworkManagementService extends INetworkManagementService.Stub {
         }
     }
 
+    public void addRoute(String interfaceName, RouteInfo route) {
+        modifyRoute(interfaceName, ADD, route);
+    }
+
+    public void removeRoute(String interfaceName, RouteInfo route) {
+        modifyRoute(interfaceName, REMOVE, route);
+    }
+
+    private void modifyRoute(String interfaceName, int action, RouteInfo route) {
+        ArrayList<String> rsp;
+
+        StringBuilder cmd;
+
+        switch (action) {
+            case ADD:
+            {
+                cmd = new StringBuilder("interface route add " + interfaceName);
+                break;
+            }
+            case REMOVE:
+            {
+                cmd = new StringBuilder("interface route remove " + interfaceName);
+                break;
+            }
+            default:
+                throw new IllegalStateException("Unknown action type " + action);
+        }
+
+        // create triplet: dest-ip-addr prefixlength gateway-ip-addr
+        LinkAddress la = route.getDestination();
+        cmd.append(' ');
+        cmd.append(la.getAddress().getHostAddress());
+        cmd.append(' ');
+        cmd.append(la.getNetworkPrefixLength());
+        cmd.append(' ');
+        if (route.getGateway() == null) {
+            if (la.getAddress() instanceof Inet4Address) {
+                cmd.append("0.0.0.0");
+            } else {
+                cmd.append ("::0");
+            }
+        } else {
+            cmd.append(route.getGateway().getHostAddress());
+        }
+        try {
+            rsp = mConnector.doCommand(cmd.toString());
+        } catch (NativeDaemonConnectorException e) {
+            throw new IllegalStateException(
+                    "Unable to communicate with native dameon to add routes - "
+                    + e);
+        }
+
+        for (String line : rsp) {
+            Log.v(TAG, "add route response is " + line);
+        }
+    }
+
+    private ArrayList<String> readRouteList(String filename) {
+        FileInputStream fstream = null;
+        ArrayList<String> list = new ArrayList<String>();
+
+        try {
+            fstream = new FileInputStream(filename);
+            DataInputStream in = new DataInputStream(fstream);
+            BufferedReader br = new BufferedReader(new InputStreamReader(in));
+            String s;
+
+            // throw away the title line
+
+            while (((s = br.readLine()) != null) && (s.length() != 0)) {
+                list.add(s);
+            }
+        } catch (IOException ex) {
+            // return current list, possibly empty
+        } finally {
+            if (fstream != null) {
+                try {
+                    fstream.close();
+                } catch (IOException ex) {}
+            }
+        }
+
+        return list;
+    }
+
+    public RouteInfo[] getRoutes(String interfaceName) {
+        ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>();
+
+        // v4 routes listed as:
+        // iface dest-addr gateway-addr flags refcnt use metric netmask mtu window IRTT
+        for (String s : readRouteList("/proc/net/route")) {
+            String[] fields = s.split("\t");
+
+            if (fields.length > 7) {
+                String iface = fields[0];
+
+                if (interfaceName.equals(iface)) {
+                    String dest = fields[1];
+                    String gate = fields[2];
+                    String flags = fields[3]; // future use?
+                    String mask = fields[7];
+                    try {
+                        // address stored as a hex string, ex: 0014A8C0
+                        InetAddress destAddr =
+                                NetworkUtils.intToInetAddress((int)Long.parseLong(dest, 16));
+                        int prefixLength =
+                                NetworkUtils.netmaskIntToPrefixLength(
+                                (int)Long.parseLong(mask, 16));
+                        LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
+
+                        // address stored as a hex string, ex 0014A8C0
+                        InetAddress gatewayAddr =
+                                NetworkUtils.intToInetAddress((int)Long.parseLong(gate, 16));
+
+                        RouteInfo route = new RouteInfo(linkAddress, gatewayAddr);
+                        routes.add(route);
+                    } catch (Exception e) {
+                        Log.e(TAG, "Error parsing route " + s + " : " + e);
+                        continue;
+                    }
+                }
+            }
+        }
+
+        // v6 routes listed as:
+        // dest-addr prefixlength ?? ?? gateway-addr ?? ?? ?? ?? iface
+        for (String s : readRouteList("/proc/net/ipv6_route")) {
+            String[]fields = s.split("\\s+");
+            if (fields.length > 9) {
+                String iface = fields[9].trim();
+                if (interfaceName.equals(iface)) {
+                    String dest = fields[0];
+                    String prefix = fields[1];
+                    String gate = fields[4];
+
+                    try {
+                        // prefix length stored as a hex string, ex 40
+                        int prefixLength = Integer.parseInt(prefix, 16);
+
+                        // address stored as a 32 char hex string
+                        // ex fe800000000000000000000000000000
+                        InetAddress destAddr = NetworkUtils.hexToInet6Address(dest);
+                        LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
+
+                        InetAddress gateAddr = NetworkUtils.hexToInet6Address(gate);
+
+                        RouteInfo route = new RouteInfo(linkAddress, gateAddr);
+                        routes.add(route);
+                    } catch (Exception e) {
+                        Log.e(TAG, "Error parsing route " + s + " : " + e);
+                        continue;
+                    }
+                }
+            }
+        }
+        return (RouteInfo[]) routes.toArray(new RouteInfo[0]);
+    }
+
     public void shutdown() {
         if (mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.SHUTDOWN)
index eb14180..a8d40b7 100644 (file)
@@ -546,7 +546,6 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
         }
 
         Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
-        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
         Bundle data = new Bundle();
         state.fillInNotifierBundle(data);
         intent.putExtras(data);
@@ -586,7 +585,6 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
         }
 
         Intent intent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
-        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
         intent.putExtra(Phone.STATE_KEY, DefaultPhoneNotifier.convertCallState(state).toString());
         if (!TextUtils.isEmpty(incomingNumber)) {
             intent.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber);
@@ -602,7 +600,6 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
         // status bar takes care of that after taking into account all of the
         // required info.
         Intent intent = new Intent(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
-        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
         intent.putExtra(Phone.STATE_KEY, DefaultPhoneNotifier.convertDataState(state).toString());
         if (!isDataConnectivityPossible) {
             intent.putExtra(Phone.NETWORK_UNAVAILABLE_KEY, true);
@@ -627,7 +624,6 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
 
     private void broadcastDataConnectionFailed(String reason, String apnType) {
         Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
-        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
         intent.putExtra(Phone.FAILURE_REASON_KEY, reason);
         intent.putExtra(Phone.DATA_APN_TYPE_KEY, apnType);
         mContext.sendStickyBroadcast(intent);
index 6e88490..fceaa4e 100644 (file)
@@ -235,6 +235,15 @@ public class WifiService extends IWifiManager.Stub {
                     }
                     break;
                 }
+                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+                    if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
+                        Slog.d(TAG, "Send failed, client connection lost");
+                    } else {
+                        Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
+                    }
+                    mClients.remove((AsyncChannel) msg.obj);
+                    break;
+                }
                 case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
                     AsyncChannel ac = new AsyncChannel();
                     ac.connect(mContext, this, msg.replyTo);
@@ -312,6 +321,13 @@ public class WifiService extends IWifiManager.Stub {
                     }
                     break;
                 }
+                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+                    Slog.e(TAG, "WifiStateMachine channel lost, msg.arg1 =" + msg.arg1);
+                    mWifiStateMachineChannel = null;
+                    //Re-establish connection to state machine
+                    mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler());
+                    break;
+                }
                 default: {
                     Slog.d(TAG, "WifiStateMachineHandler.handleMessage ignoring msg=" + msg);
                     break;
@@ -584,7 +600,12 @@ public class WifiService extends IWifiManager.Stub {
      */
     public WifiConfiguration getWifiApConfiguration() {
         enforceAccessPermission();
-        return mWifiStateMachine.syncGetWifiApConfiguration(mWifiStateMachineChannel);
+        if (mWifiStateMachineChannel != null) {
+            return mWifiStateMachine.syncGetWifiApConfiguration(mWifiStateMachineChannel);
+        } else {
+            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
+            return null;
+        }
     }
 
     /**
index 5853696..2290801 100644 (file)
@@ -49,14 +49,17 @@ import android.provider.Settings;
 import android.util.Log;
 
 import com.android.internal.telephony.Phone;
-import com.android.internal.util.HierarchicalState;
-import com.android.internal.util.HierarchicalStateMachine;
+import com.android.internal.util.IState;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.net.InetAddress;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.Set;
 /**
@@ -81,7 +84,15 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
     private String[] mTetherableUsbRegexs;
     private String[] mTetherableWifiRegexs;
     private String[] mTetherableBluetoothRegexs;
-    private String[] mUpstreamIfaceRegexs;
+    private Collection<Integer> mUpstreamIfaceTypes;
+
+    private static final Integer MOBILE_TYPE = new Integer(ConnectivityManager.TYPE_MOBILE);
+    private static final Integer HIPRI_TYPE = new Integer(ConnectivityManager.TYPE_MOBILE_HIPRI);
+    private static final Integer DUN_TYPE = new Integer(ConnectivityManager.TYPE_MOBILE_DUN);
+
+    // if we have to connect to mobile, what APN type should we use?  Calculated by examining the
+    // upstream type list and the DUN_REQUIRED secure-setting
+    private int mPreferredUpstreamMobileApn = ConnectivityManager.TYPE_NONE;
 
     private Looper mLooper;
     private HandlerThread mThread;
@@ -119,10 +130,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
     private static final String DNS_DEFAULT_SERVER1 = "8.8.8.8";
     private static final String DNS_DEFAULT_SERVER2 = "8.8.4.4";
 
-    // resampled each time we turn on tethering - used as cache for settings/config-val
-    private boolean mDunRequired;  // configuration info - must use DUN apn on 3g
-
-    private HierarchicalStateMachine mTetherMasterSM;
+    private StateMachine mTetherMasterSM;
 
     private Notification mTetheredNotification;
 
@@ -188,7 +196,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
             mDhcpRange[12] = DHCP_DEFAULT_RANGE7_START;
             mDhcpRange[13] = DHCP_DEFAULT_RANGE7_STOP;
         }
-        mDunRequired = false; // resample when we turn on
 
         mTetherableUsbRegexs = context.getResources().getStringArray(
                 com.android.internal.R.array.config_tether_usb_regexs);
@@ -196,8 +203,15 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
                 com.android.internal.R.array.config_tether_wifi_regexs);
         mTetherableBluetoothRegexs = context.getResources().getStringArray(
                 com.android.internal.R.array.config_tether_bluetooth_regexs);
-        mUpstreamIfaceRegexs = context.getResources().getStringArray(
-                com.android.internal.R.array.config_tether_upstream_regexs);
+        int ifaceTypes[] = context.getResources().getIntArray(
+                com.android.internal.R.array.config_tether_upstream_types);
+        mUpstreamIfaceTypes = new ArrayList();
+        for (int i : ifaceTypes) {
+            mUpstreamIfaceTypes.add(new Integer(i));
+        }
+
+        // check if the upstream type list needs to be modified due to secure-settings
+        checkDunRequired();
 
         // TODO - remove and rely on real notifications of the current iface
         mDnsServers = new String[2];
@@ -601,16 +615,44 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
         return mTetherableBluetoothRegexs;
     }
 
-    public String[] getUpstreamIfaceRegexs() {
-        return mUpstreamIfaceRegexs;
+    public int[] getUpstreamIfaceTypes() {
+        int values[] = new int[mUpstreamIfaceTypes.size()];
+        Iterator<Integer> iterator = mUpstreamIfaceTypes.iterator();
+        for (int i=0; i < mUpstreamIfaceTypes.size(); i++) {
+            values[i] = iterator.next();
+        }
+        return values;
     }
 
-    public boolean isDunRequired() {
-        boolean defaultVal = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_tether_dun_required);
-        boolean result = (Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.TETHER_DUN_REQUIRED, (defaultVal ? 1 : 0)) == 1);
-        return result;
+    public void checkDunRequired() {
+        int requiredApn = ((Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.TETHER_DUN_REQUIRED, 0) == 1) ?
+                ConnectivityManager.TYPE_MOBILE_DUN :
+                ConnectivityManager.TYPE_MOBILE_HIPRI);
+        if (mPreferredUpstreamMobileApn != requiredApn) {
+            if (requiredApn == ConnectivityManager.TYPE_MOBILE_DUN) {
+                while (mUpstreamIfaceTypes.contains(MOBILE_TYPE)) {
+                    mUpstreamIfaceTypes.remove(MOBILE_TYPE);
+                }
+                while (mUpstreamIfaceTypes.contains(HIPRI_TYPE)) {
+                    mUpstreamIfaceTypes.remove(HIPRI_TYPE);
+                }
+                if (mUpstreamIfaceTypes.contains(DUN_TYPE) == false) {
+                    mUpstreamIfaceTypes.add(DUN_TYPE);
+                }
+            } else {
+                while (mUpstreamIfaceTypes.contains(DUN_TYPE)) {
+                    mUpstreamIfaceTypes.remove(DUN_TYPE);
+                }
+                if (mUpstreamIfaceTypes.contains(MOBILE_TYPE) == false) {
+                    mUpstreamIfaceTypes.add(MOBILE_TYPE);
+                }
+                if (mUpstreamIfaceTypes.contains(HIPRI_TYPE) == false) {
+                    mUpstreamIfaceTypes.add(HIPRI_TYPE);
+                }
+            }
+            mPreferredUpstreamMobileApn = requiredApn;
+        }
     }
 
     public String[] getTetheredIfaces() {
@@ -667,8 +709,15 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
         return retVal;
     }
 
+    //TODO: Temporary handling upstream change triggered without
+    //      CONNECTIVITY_ACTION. Only to accomodate interface
+    //      switch during HO.
+    //      @see bug/4455071
+    public void handleTetherIfaceChange() {
+        mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED);
+    }
 
-    class TetherInterfaceSM extends HierarchicalStateMachine {
+    class TetherInterfaceSM extends StateMachine {
         // notification from the master SM that it's not in tether mode
         static final int CMD_TETHER_MODE_DEAD            =  1;
         // request from the user that it wants to tether
@@ -694,13 +743,13 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
         // the upstream connection has changed
         static final int CMD_TETHER_CONNECTION_CHANGED   = 12;
 
-        private HierarchicalState mDefaultState;
+        private State mDefaultState;
 
-        private HierarchicalState mInitialState;
-        private HierarchicalState mStartingState;
-        private HierarchicalState mTetheredState;
+        private State mInitialState;
+        private State mStartingState;
+        private State mTetheredState;
 
-        private HierarchicalState mUnavailableState;
+        private State mUnavailableState;
 
         private boolean mAvailable;
         private boolean mTethered;
@@ -732,7 +781,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
         public String toString() {
             String res = new String();
             res += mIfaceName + " - ";
-            HierarchicalState current = getCurrentState();
+            IState current = getCurrentState();
             if (current == mInitialState) res += "InitialState";
             if (current == mStartingState) res += "StartingState";
             if (current == mTetheredState) res += "TetheredState";
@@ -782,7 +831,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
             return (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR);
         }
 
-        class InitialState extends HierarchicalState {
+        class InitialState extends State {
             @Override
             public void enter() {
                 setAvailable(true);
@@ -812,7 +861,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
             }
         }
 
-        class StartingState extends HierarchicalState {
+        class StartingState extends State {
             @Override
             public void enter() {
                 setAvailable(false);
@@ -870,7 +919,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
             }
         }
 
-        class TetheredState extends HierarchicalState {
+        class TetheredState extends State {
             @Override
             public void enter() {
                 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
@@ -1034,7 +1083,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
             }
         }
 
-        class UnavailableState extends HierarchicalState {
+        class UnavailableState extends State {
             @Override
             public void enter() {
                 setAvailable(false);
@@ -1064,7 +1113,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
 
     }
 
-    class TetherMasterSM extends HierarchicalStateMachine {
+    class TetherMasterSM extends StateMachine {
         // an interface SM has requested Tethering
         static final int CMD_TETHER_MODE_REQUESTED   = 1;
         // an interface SM has unrequested Tethering
@@ -1082,19 +1131,19 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
         // We do not flush the old ones.
         private int mSequenceNumber;
 
-        private HierarchicalState mInitialState;
-        private HierarchicalState mTetherModeAliveState;
+        private State mInitialState;
+        private State mTetherModeAliveState;
 
-        private HierarchicalState mSetIpForwardingEnabledErrorState;
-        private HierarchicalState mSetIpForwardingDisabledErrorState;
-        private HierarchicalState mStartTetheringErrorState;
-        private HierarchicalState mStopTetheringErrorState;
-        private HierarchicalState mSetDnsForwardersErrorState;
+        private State mSetIpForwardingEnabledErrorState;
+        private State mSetIpForwardingDisabledErrorState;
+        private State mStartTetheringErrorState;
+        private State mStopTetheringErrorState;
+        private State mSetDnsForwardersErrorState;
 
         private ArrayList mNotifyList;
 
         private int mCurrentConnectionSequence;
-        private boolean mMobileReserved = false;
+        private int mMobileApnReserved = ConnectivityManager.TYPE_NONE;
 
         private String mUpstreamIfaceName = null;
 
@@ -1125,7 +1174,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
             setInitialState(mInitialState);
         }
 
-        class TetherMasterUtilState extends HierarchicalState {
+        class TetherMasterUtilState extends State {
             protected final static boolean TRY_TO_SETUP_MOBILE_CONNECTION = true;
             protected final static boolean WAIT_FOR_NETWORK_TO_SETTLE     = false;
 
@@ -1133,22 +1182,34 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
             public boolean processMessage(Message m) {
                 return false;
             }
-            protected boolean turnOnMobileConnection() {
+            protected String enableString(int apnType) {
+                switch (apnType) {
+                case ConnectivityManager.TYPE_MOBILE_DUN:
+                    return Phone.FEATURE_ENABLE_DUN_ALWAYS;
+                case ConnectivityManager.TYPE_MOBILE:
+                case ConnectivityManager.TYPE_MOBILE_HIPRI:
+                    return Phone.FEATURE_ENABLE_HIPRI;
+                }
+                return null;
+            }
+            protected boolean turnOnUpstreamMobileConnection(int apnType) {
                 boolean retValue = true;
-                if (mMobileReserved) return retValue;
+                if (apnType == ConnectivityManager.TYPE_NONE) return false;
+                if (apnType != mMobileApnReserved) turnOffUpstreamMobileConnection();
                 IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
                 IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
                 int result = Phone.APN_REQUEST_FAILED;
+                String enableString = enableString(apnType);
+                if (enableString == null) return false;
                 try {
                     result = service.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
-                            (mDunRequired ? Phone.FEATURE_ENABLE_DUN_ALWAYS :
-                            Phone.FEATURE_ENABLE_HIPRI), new Binder());
+                            enableString, new Binder());
                 } catch (Exception e) {
                 }
                 switch (result) {
                 case Phone.APN_ALREADY_ACTIVE:
                 case Phone.APN_REQUEST_STARTED:
-                    mMobileReserved = true;
+                    mMobileApnReserved = apnType;
                     Message m = obtainMessage(CMD_CELL_CONNECTION_RENEW);
                     m.arg1 = ++mCurrentConnectionSequence;
                     sendMessageDelayed(m, CELL_CONNECTION_RENEW_MS);
@@ -1161,19 +1222,18 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
 
                 return retValue;
             }
-            protected boolean turnOffMobileConnection() {
-                if (mMobileReserved) {
+            protected boolean turnOffUpstreamMobileConnection() {
+                if (mMobileApnReserved != ConnectivityManager.TYPE_NONE) {
                     IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
                     IConnectivityManager service =
                             IConnectivityManager.Stub.asInterface(b);
                     try {
                         service.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
-                                (mDunRequired? Phone.FEATURE_ENABLE_DUN_ALWAYS :
-                                             Phone.FEATURE_ENABLE_HIPRI));
+                                enableString(mMobileApnReserved));
                     } catch (Exception e) {
                         return false;
                     }
-                    mMobileReserved = false;
+                    mMobileApnReserved = ConnectivityManager.TYPE_NONE;
                 }
                 return true;
             }
@@ -1225,111 +1285,55 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
                 transitionTo(mInitialState);
                 return true;
             }
-            protected String findActiveUpstreamIface() {
-                // check for what iface we can use - if none found switch to error.
+
+            protected void chooseUpstreamType(boolean tryCell) {
                 IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
                 IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b);
+                int upType = ConnectivityManager.TYPE_NONE;
+                String iface = null;
 
-                try {
-                    LinkProperties defaultProp = cm.getActiveLinkProperties();
-                    if (defaultProp != null) {
-                        String iface = defaultProp.getInterfaceName();
-                        for(String regex : mUpstreamIfaceRegexs) {
-                            if (iface.matches(regex)) return iface;
-                        }
-                    }
-                } catch (RemoteException e) { }
-
-                b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-                INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
-
-                String[] ifaces = new String[0];
-                try {
-                    ifaces = service.listInterfaces();
-                } catch (Exception e) {
-                    Log.e(TAG, "Error listing Interfaces :" + e);
-                    return null;
-                }
-
-                for (String iface : ifaces) {
-                    for (String regex : mUpstreamIfaceRegexs) {
-                        if (iface.matches(regex)) {
-                            // verify it is active
-                            InterfaceConfiguration ifcg = null;
-                            try {
-                                ifcg = service.getInterfaceConfig(iface);
-                                if (ifcg.isActive()) {
-                                    return iface;
-                                }
-                            } catch (Exception e) {
-                                Log.e(TAG, "Error getting iface config :" + e);
-                                // ignore - try next
-                                continue;
-                            }
-                        }
+                for (Integer netType : mUpstreamIfaceTypes) {
+                    NetworkInfo info = null;
+                    try {
+                        info = cm.getNetworkInfo(netType.intValue());
+                    } catch (RemoteException e) { }
+                    if ((info != null) && info.isConnected()) {
+                        upType = netType.intValue();
+                        break;
                     }
                 }
-                return null;
-            }
 
-            protected void chooseUpstreamType(boolean tryCell) {
-                // decide if the current upstream is good or not and if not
-                // do something about it (start up DUN if required or HiPri if not)
-                String iface = findActiveUpstreamIface();
-                IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
-                IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b);
-                mMobileReserved = false;
                 if (DEBUG) {
-                    Log.d(TAG, "chooseUpstreamType(" + tryCell + "),  dunRequired ="
-                            + mDunRequired + ", iface=" + iface);
+                    Log.d(TAG, "chooseUpstreamType(" + tryCell + "), preferredApn ="
+                            + mPreferredUpstreamMobileApn + ", got type=" + upType);
                 }
-                if (iface != null) {
-                    try {
-                        if (mDunRequired) {
-                            // check if Dun is on - we can use that
-                            NetworkInfo info = cm.getNetworkInfo(
-                                    ConnectivityManager.TYPE_MOBILE_DUN);
-                            if (info.isConnected()) {
-                                if (DEBUG) Log.d(TAG, "setting dun ifacename =" + iface);
-                                // even if we're already connected - it may be somebody else's
-                                // refcount, so add our own
-                                turnOnMobileConnection();
-                            } else {
-                                // verify the iface is not the default mobile - can't use that!
-                                info = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
-                                if (info.isConnected()) {
-                                    iface = null; // can't accept this one
-                                }
-                            }
-                        } else {
-                            if (DEBUG) Log.d(TAG, "checking if hipri brought us this connection");
-                            NetworkInfo info = cm.getNetworkInfo(
-                                    ConnectivityManager.TYPE_MOBILE_HIPRI);
-                            if (info.isConnected()) {
-                                if (DEBUG) Log.d(TAG, "yes - hipri in use");
-                                // even if we're already connected - it may be sombody else's
-                                // refcount, so add our own
-                                turnOnMobileConnection();
-                            }
-                        }
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "RemoteException calling ConnectivityManager " + e);
-                        iface = null;
-                    }
+
+                // if we're on DUN, put our own grab on it
+                if (upType == ConnectivityManager.TYPE_MOBILE_DUN ||
+                        upType == ConnectivityManager.TYPE_MOBILE_HIPRI) {
+                    turnOnUpstreamMobileConnection(upType);
                 }
-                // may have been set to null in the if above
-                if (iface == null ) {
-                    boolean success = false;
-                    if (tryCell == TRY_TO_SETUP_MOBILE_CONNECTION) {
-                        success = turnOnMobileConnection();
+
+                if (upType == ConnectivityManager.TYPE_NONE) {
+                    boolean tryAgainLater = true;
+                    if ((tryCell == TRY_TO_SETUP_MOBILE_CONNECTION) &&
+                            (turnOnUpstreamMobileConnection(mPreferredUpstreamMobileApn) == true)) {
+                        // we think mobile should be coming up - don't set a retry
+                        tryAgainLater = false;
                     }
-                    if (!success) {
-                        // wait for things to settle and retry
+                    if (tryAgainLater) {
                         sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
                     }
+                } else {
+                    LinkProperties linkProperties = null;
+                    try {
+                        linkProperties = cm.getLinkProperties(upType);
+                    } catch (RemoteException e) { }
+                    if (linkProperties != null) iface = linkProperties.getInterfaceName();
                 }
                 notifyTetheredOfNewUpstreamIface(iface);
             }
+
             protected void notifyTetheredOfNewUpstreamIface(String ifaceName) {
                 if (DEBUG) Log.d(TAG, "notifying tethered with iface =" + ifaceName);
                 mUpstreamIfaceName = ifaceName;
@@ -1344,7 +1348,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
         class InitialState extends TetherMasterUtilState {
             @Override
             public void enter() {
-                mMobileReserved = false;
             }
             @Override
             public boolean processMessage(Message message) {
@@ -1352,7 +1355,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
                 boolean retValue = true;
                 switch (message.what) {
                     case CMD_TETHER_MODE_REQUESTED:
-                        mDunRequired = isDunRequired();
+                        checkDunRequired();
                         TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
                         if (DEBUG) Log.d(TAG, "Tether Mode requested by " + who.toString());
                         mNotifyList.add(who);
@@ -1386,7 +1389,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
             }
             @Override
             public void exit() {
-                turnOffMobileConnection();
+                turnOffUpstreamMobileConnection();
                 notifyTetheredOfNewUpstreamIface(null);
             }
             @Override
@@ -1424,23 +1427,22 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
                                 Log.d(TAG, "renewing mobile connection - requeuing for another " +
                                         CELL_CONNECTION_RENEW_MS + "ms");
                             }
-                            mMobileReserved = false; // need to renew it
-                            turnOnMobileConnection();
+                            turnOnUpstreamMobileConnection(mMobileApnReserved);
                         }
                         break;
-                   case CMD_RETRY_UPSTREAM:
-                       chooseUpstreamType(mTryCell);
-                       mTryCell = !mTryCell;
-                       break;
-                   default:
-                       retValue = false;
-                       break;
+                    case CMD_RETRY_UPSTREAM:
+                        chooseUpstreamType(mTryCell);
+                        mTryCell = !mTryCell;
+                        break;
+                    default:
+                        retValue = false;
+                        break;
                 }
                 return retValue;
             }
         }
 
-        class ErrorState extends HierarchicalState {
+        class ErrorState extends State {
             int mErrorNotification;
             @Override
             public boolean processMessage(Message message) {
index 290f2c1..2f010e5 100644 (file)
@@ -91,6 +91,12 @@ public class ServiceState implements Parcelable {
     public static final int RADIO_TECHNOLOGY_HSPA = 11;
     /** @hide */
     public static final int RADIO_TECHNOLOGY_EVDO_B = 12;
+    /** @hide */
+    public static final int RADIO_TECHNOLOGY_EHRPD = 13;
+    /** @hide */
+    public static final int RADIO_TECHNOLOGY_LTE = 14;
+    /** @hide */
+    public static final int RADIO_TECHNOLOGY_HSPAP = 15;
 
     /**
      * Available registration states for GSM, UMTS and CDMA.
@@ -381,53 +387,77 @@ public class ServiceState implements Parcelable {
                 && mIsEmergencyOnly == s.mIsEmergencyOnly);
     }
 
+    /**
+     * Convert radio technology to String
+     *
+     * @param radioTechnology
+     * @return String representation of the RAT
+     *
+     * @hide
+     */
+    public static String radioTechnologyToString(int rt) {
+        String rtString;
+
+        switch(rt) {
+            case 0:
+                rtString = "Unknown";
+                break;
+            case 1:
+                rtString = "GPRS";
+                break;
+            case 2:
+                rtString = "EDGE";
+                break;
+            case 3:
+                rtString = "UMTS";
+                break;
+            case 4:
+                rtString = "CDMA-IS95A";
+                break;
+            case 5:
+                rtString = "CDMA-IS95B";
+                break;
+            case 6:
+                rtString = "1xRTT";
+                break;
+            case 7:
+                rtString = "EvDo-rev.0";
+                break;
+            case 8:
+                rtString = "EvDo-rev.A";
+                break;
+            case 9:
+                rtString = "HSDPA";
+                break;
+            case 10:
+                rtString = "HSUPA";
+                break;
+            case 11:
+                rtString = "HSPA";
+                break;
+            case 12:
+                rtString = "EvDo-rev.B";
+                break;
+            case 13:
+                rtString = "eHRPD";
+                break;
+            case 14:
+                rtString = "LTE";
+                break;
+            case 15:
+                rtString = "HSPAP";
+                break;
+            default:
+                rtString = "Unexpected";
+                Log.w(LOG_TAG, "Unexpected radioTechnology=" + rt);
+                break;
+        }
+        return rtString + ":" + rt;
+    }
+
     @Override
     public String toString() {
-        String radioTechnology = new String("Error in radioTechnology");
-        switch(this.mRadioTechnology) {
-        case 0:
-            radioTechnology = "Unknown";
-            break;
-        case 1:
-            radioTechnology = "GPRS";
-            break;
-        case 2:
-            radioTechnology = "EDGE";
-            break;
-        case 3:
-            radioTechnology = "UMTS";
-            break;
-        case 4:
-            radioTechnology = "IS95A";
-            break;
-        case 5:
-            radioTechnology = "IS95B";
-            break;
-        case 6:
-            radioTechnology = "1xRTT";
-            break;
-        case 7:
-            radioTechnology = "EvDo rev. 0";
-            break;
-        case 8:
-            radioTechnology = "EvDo rev. A";
-            break;
-        case 9:
-            radioTechnology = "HSDPA";
-            break;
-        case 10:
-            radioTechnology = "HSUPA";
-            break;
-        case 11:
-            radioTechnology = "HSPA";
-            break;
-        case 12:
-            radioTechnology = "EvDo rev. B";
-            break;
-        default:
-            Log.w(LOG_TAG, "mRadioTechnology variable out of range.");
-        break;
-        }
+        String radioTechnology = radioTechnologyToString(mRadioTechnology);
 
         return (mState + " " + (mRoaming ? "roaming" : "home")
                 + " " + mOperatorAlphaLong
@@ -443,8 +473,8 @@ public class ServiceState implements Parcelable {
                 + " EmergOnly=" + mIsEmergencyOnly);
     }
 
-    public void setStateOutOfService() {
-        mState = STATE_OUT_OF_SERVICE;
+    private void setNullState(int state) {
+        mState = state;
         mRoaming = false;
         mOperatorAlphaLong = null;
         mOperatorAlphaShort = null;
@@ -461,23 +491,12 @@ public class ServiceState implements Parcelable {
         mIsEmergencyOnly = false;
     }
 
-    // TODO - can't this be combined with the above method?
+    public void setStateOutOfService() {
+        setNullState(STATE_OUT_OF_SERVICE);
+    }
+
     public void setStateOff() {
-        mState = STATE_POWER_OFF;
-        mRoaming = false;
-        mOperatorAlphaLong = null;
-        mOperatorAlphaShort = null;
-        mOperatorNumeric = null;
-        mIsManualNetworkSelection = false;
-        mRadioTechnology = 0;
-        mCssIndicator = false;
-        mNetworkId = -1;
-        mSystemId = -1;
-        mCdmaRoamingIndicator = -1;
-        mCdmaDefaultRoamingIndicator = -1;
-        mCdmaEriIconIndex = -1;
-        mCdmaEriIconMode = -1;
-        mIsEmergencyOnly = false;
+        setNullState(STATE_POWER_OFF);
     }
 
     public void setState(int state) {
@@ -536,7 +555,7 @@ public class ServiceState implements Parcelable {
      *
      * @hide
      */
-    public void setCdmaEriText(String longName) {
+    public void setOperatorAlphaLong(String longName) {
         mOperatorAlphaLong = longName;
     }
 
index 1767dd9..a88825b 100644 (file)
@@ -556,10 +556,7 @@ public class SignalStrength implements Parcelable {
      * @hide
      */
     public int getLteDbm() {
-        log("TODO: teach getLteDbm to compute dBm properly");
-        int level = -1;
-        if (DBG) log("getLteDbm=" + level);
-        return level;
+        return mLteRsrp;
     }
 
     /**
@@ -568,22 +565,33 @@ public class SignalStrength implements Parcelable {
      * @hide
      */
     public int getLteLevel() {
-        log("TODO: teach getLteLevel to compute Level properly");
-        int level = SIGNAL_STRENGTH_MODERATE;
-        if (DBG) log("getLteLevel=" + level);
-        return level;
+        int levelLteRsrp = 0;
+
+        if (mLteRsrp == -1) levelLteRsrp = 0;
+        else if (mLteRsrp >= -85) levelLteRsrp = SIGNAL_STRENGTH_GREAT;
+        else if (mLteRsrp >= -95) levelLteRsrp = SIGNAL_STRENGTH_GOOD;
+        else if (mLteRsrp >= -105) levelLteRsrp = SIGNAL_STRENGTH_MODERATE;
+        else if (mLteRsrp >= -115) levelLteRsrp = SIGNAL_STRENGTH_POOR;
+        else levelLteRsrp = 0;
+
+        if (DBG) log("Lte level: "+levelLteRsrp);
+        return levelLteRsrp;
     }
 
     /**
-     * Get the LTE signal level as an asu value between 0..31, 99 is unknown
+     * Get the LTE signal level as an asu value between 0..97, 99 is unknown
+     * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
      *
      * @hide
      */
     public int getLteAsuLevel() {
-        log("TODO: teach getLteAsuLevel to compute asu Level properly");
-        int level = 4;
-        if (DBG) log("getLteAsuLevel=" + level);
-        return level;
+        int lteAsuLevel = 99;
+        int lteDbm = getLteDbm();
+        if (lteDbm <= -140) lteAsuLevel = 0;
+        else if (lteDbm >= -43) lteAsuLevel = 97;
+        else lteAsuLevel = lteDbm + 140;
+        if (DBG) log("Lte Asu level: "+lteAsuLevel);
+        return lteAsuLevel;
     }
 
     /**
index 5a417b3..acbf08b 100644 (file)
@@ -30,7 +30,6 @@ import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.ITelephonyRegistry;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.TelephonyProperties;
 
 import java.util.List;
@@ -632,6 +631,28 @@ public class TelephonyManager {
         }
     }
 
+    /**
+     * Return if the current radio is LTE on CDMA. This
+     * is a tri-state return value as for a period of time
+     * the mode may be unknown.
+     *
+     * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
+     * or {@link Phone#LTE_ON_CDMA_TRUE}
+     *
+     * @hide
+     */
+    public int getLteOnCdmaMode() {
+        try {
+            return getITelephony().getLteOnCdmaMode();
+        } catch (RemoteException ex) {
+            // Assume no ICC card if remote exception which shouldn't happen
+            return Phone.LTE_ON_CDMA_UNKNOWN;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return Phone.LTE_ON_CDMA_UNKNOWN;
+        }
+    }
+
     //
     //
     // Subscriber Info
diff --git a/telephony/java/com/android/internal/telephony/ApnContext.java b/telephony/java/com/android/internal/telephony/ApnContext.java
new file mode 100644 (file)
index 0000000..a52f1ca
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * 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;
+
+import android.util.Log;
+import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Maintain the Apn context
+ */
+public class ApnContext {
+
+    public final String LOG_TAG;
+
+    protected static final boolean DBG = true;
+
+    private final String mApnType;
+
+    private DataConnectionTracker.State mState;
+
+    private ArrayList<ApnSetting> mWaitingApns = null;
+
+    /** A zero indicates that all waiting APNs had a permanent error */
+    private AtomicInteger mWaitingApnsPermanentFailureCountDown;
+
+    private ApnSetting mApnSetting;
+
+    DataConnection mDataConnection;
+
+    DataConnectionAc mDataConnectionAc;
+
+    String mReason;
+
+    /**
+     * user/app requested connection on this APN
+     */
+    AtomicBoolean mDataEnabled;
+
+    /**
+     * carrier requirements met
+     */
+    AtomicBoolean mDependencyMet;
+
+    public ApnContext(String apnType, String logTag) {
+        mApnType = apnType;
+        mState = DataConnectionTracker.State.IDLE;
+        setReason(Phone.REASON_DATA_ENABLED);
+        mDataEnabled = new AtomicBoolean(false);
+        mDependencyMet = new AtomicBoolean(true);
+        mWaitingApnsPermanentFailureCountDown = new AtomicInteger(0);
+        LOG_TAG = logTag;
+    }
+
+    public String getApnType() {
+        return mApnType;
+    }
+
+    public synchronized DataConnection getDataConnection() {
+        return mDataConnection;
+    }
+
+    public synchronized void setDataConnection(DataConnection dataConnection) {
+        mDataConnection = dataConnection;
+    }
+
+
+    public synchronized DataConnectionAc getDataConnectionAc() {
+        return mDataConnectionAc;
+    }
+
+    public synchronized void setDataConnectionAc(DataConnectionAc dcac) {
+        if (dcac != null) {
+            dcac.addApnContextSync(this);
+        } else {
+            if (mDataConnectionAc != null) mDataConnectionAc.removeApnContextSync(this);
+        }
+        mDataConnectionAc = dcac;
+    }
+
+    public synchronized ApnSetting getApnSetting() {
+        return mApnSetting;
+    }
+
+    public synchronized void setApnSetting(ApnSetting apnSetting) {
+        mApnSetting = apnSetting;
+    }
+
+    public synchronized void setWaitingApns(ArrayList<ApnSetting> waitingApns) {
+        mWaitingApns = waitingApns;
+        mWaitingApnsPermanentFailureCountDown.set(mWaitingApns.size());
+    }
+
+    public int getWaitingApnsPermFailCount() {
+        return mWaitingApnsPermanentFailureCountDown.get();
+    }
+
+    public void decWaitingApnsPermFailCount() {
+        mWaitingApnsPermanentFailureCountDown.decrementAndGet();
+    }
+
+    public synchronized ApnSetting getNextWaitingApn() {
+        ArrayList<ApnSetting> list = mWaitingApns;
+        ApnSetting apn = null;
+
+        if (list != null) {
+            if (!list.isEmpty()) {
+                apn = list.get(0);
+            }
+        }
+        return apn;
+    }
+
+    public synchronized void removeNextWaitingApn() {
+        if ((mWaitingApns != null) && (!mWaitingApns.isEmpty())) {
+            mWaitingApns.remove(0);
+        }
+    }
+
+    public synchronized ArrayList<ApnSetting> getWaitingApns() {
+        return mWaitingApns;
+    }
+
+    public synchronized void setState(DataConnectionTracker.State s) {
+        if (DBG) {
+            log("setState: " + s + " for type " + mApnType + ", previous state:" + mState);
+        }
+
+        mState = s;
+
+        if (mState == DataConnectionTracker.State.FAILED) {
+            if (mWaitingApns != null) {
+                mWaitingApns.clear(); // when teardown the connection and set to IDLE
+            }
+        }
+    }
+
+    public synchronized DataConnectionTracker.State getState() {
+        return mState;
+    }
+
+    public synchronized void setReason(String reason) {
+        if (DBG) {
+            log("set reason as " + reason + ", for type " + mApnType + ",current state " + mState);
+        }
+        mReason = reason;
+    }
+
+    public synchronized String getReason() {
+        return mReason;
+    }
+
+    public boolean isReady() {
+        return mDataEnabled.get() && mDependencyMet.get();
+    }
+
+    public void setEnabled(boolean enabled) {
+        if (DBG) {
+            log("set enabled as " + enabled + ", for type " +
+                    mApnType + ", current state is " + mDataEnabled.get());
+        }
+        mDataEnabled.set(enabled);
+    }
+
+    public boolean isEnabled() {
+        return mDataEnabled.get();
+    }
+
+    public void setDependencyMet(boolean met) {
+        if (DBG) {
+            log("set mDependencyMet as " + met + ", for type " + mApnType +
+                    ", current state is " + mDependencyMet.get());
+        }
+        mDependencyMet.set(met);
+    }
+
+    public boolean getDependencyMet() {
+       return mDependencyMet.get();
+    }
+
+    @Override
+    public String toString() {
+        return "state=" + getState() + " apnType=" + mApnType;
+    }
+
+    protected void log(String s) {
+        Log.d(LOG_TAG, "[ApnContext] " + s);
+    }
+}
index 20dbaf3..b88bcf7 100644 (file)
@@ -23,21 +23,21 @@ public class ApnSetting {
 
     static final String V2_FORMAT_REGEX = "^\\[ApnSettingV2\\]\\s*";
 
-    public String carrier;
-    public String apn;
-    public String proxy;
-    public String port;
-    public String mmsc;
-    public String mmsProxy;
-    public String mmsPort;
-    public String user;
-    public String password;
-    public int authType;
-    public String[] types;
-    public int id;
-    public String numeric;
-    public String protocol;
-    public String roamingProtocol;
+    public final String carrier;
+    public final String apn;
+    public final String proxy;
+    public final String port;
+    public final String mmsc;
+    public final String mmsProxy;
+    public final String mmsPort;
+    public final String user;
+    public final String password;
+    public final int authType;
+    public final String[] types;
+    public final int id;
+    public final String numeric;
+    public final String protocol;
+    public final String roamingProtocol;
 
     public ApnSetting(int id, String numeric, String carrier, String apn,
             String proxy, String port,
index 9b19600..13afbb7 100644 (file)
@@ -22,9 +22,15 @@ import android.os.RegistrantList;
 import android.os.Registrant;
 import android.os.Handler;
 import android.os.AsyncResult;
+import android.os.SystemProperties;
 import android.util.Config;
 import android.util.Log;
 
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 /**
  * {@hide}
  */
@@ -34,6 +40,9 @@ public abstract class BaseCommands implements CommandsInterface {
     //***** Instance Variables
     protected Context mContext;
     protected RadioState mState = RadioState.RADIO_UNAVAILABLE;
+    protected RadioState mSimState = RadioState.RADIO_UNAVAILABLE;
+    protected RadioState mRuimState = RadioState.RADIO_UNAVAILABLE;
+    protected RadioState mNvState = RadioState.RADIO_UNAVAILABLE;
     protected Object mStateMonitor = new Object();
 
     protected RegistrantList mRadioStateChangedRegistrants = new RegistrantList();
@@ -68,6 +77,8 @@ public abstract class BaseCommands implements CommandsInterface {
     protected RegistrantList mCdmaSubscriptionChangedRegistrants = new RegistrantList();
     protected RegistrantList mCdmaPrlChangedRegistrants = new RegistrantList();
     protected RegistrantList mExitEmergencyCallbackModeRegistrants = new RegistrantList();
+    protected RegistrantList mRilConnectedRegistrants = new RegistrantList();
+    protected RegistrantList mIccRefreshRegistrants = new RegistrantList();
 
     protected Registrant mSMSRegistrant;
     protected Registrant mNITZTimeRegistrant;
@@ -82,18 +93,20 @@ public abstract class BaseCommands implements CommandsInterface {
     protected Registrant mCatCallSetUpRegistrant;
     protected Registrant mIccSmsFullRegistrant;
     protected Registrant mEmergencyCallbackModeRegistrant;
-    protected Registrant mIccRefreshRegistrant;
     protected Registrant mRingRegistrant;
     protected Registrant mRestrictedStateRegistrant;
     protected Registrant mGsmBroadcastSmsRegistrant;
 
-    // Network Mode received from PhoneFactory
-    protected int mNetworkMode;
+    // Preferred network type received from PhoneFactory.
+    // This is used when establishing a connection to the
+    // vendor ril so it starts up in the correct mode.
+    protected int mPreferredNetworkType;
     // CDMA subscription received from PhoneFactory
     protected int mCdmaSubscription;
     // Type of Phone, GSM or CDMA. Set by CDMAPhone or GSMPhone.
     protected int mPhoneType;
-
+    // RIL Version
+    protected int mRilVersion = -1;
 
     public BaseCommands(Context context) {
         mContext = context;  // May be null (if so we won't log statistics)
@@ -105,6 +118,18 @@ public abstract class BaseCommands implements CommandsInterface {
         return mState;
     }
 
+    public RadioState getSimState() {
+        return mSimState;
+    }
+
+    public RadioState getRuimState() {
+        return mRuimState;
+    }
+
+    public RadioState getNvState() {
+        return mNvState;
+    }
+
 
     public void registerForRadioStateChanged(Handler h, int what, Object obj) {
         Registrant r = new Registrant (h, what, obj);
@@ -200,7 +225,7 @@ public abstract class BaseCommands implements CommandsInterface {
         synchronized (mStateMonitor) {
             mSIMReadyRegistrants.add(r);
 
-            if (mState.isSIMReady()) {
+            if (mSimState.isSIMReady()) {
                 r.notifyRegistrant(new AsyncResult(null, null, null));
             }
         }
@@ -219,7 +244,7 @@ public abstract class BaseCommands implements CommandsInterface {
         synchronized (mStateMonitor) {
             mRUIMReadyRegistrants.add(r);
 
-            if (mState.isRUIMReady()) {
+            if (mRuimState.isRUIMReady()) {
                 r.notifyRegistrant(new AsyncResult(null, null, null));
             }
         }
@@ -238,7 +263,7 @@ public abstract class BaseCommands implements CommandsInterface {
         synchronized (mStateMonitor) {
             mNVReadyRegistrants.add(r);
 
-            if (mState.isNVReady()) {
+            if (mNvState.isNVReady()) {
                 r.notifyRegistrant(new AsyncResult(null, null, null));
             }
         }
@@ -256,7 +281,7 @@ public abstract class BaseCommands implements CommandsInterface {
         synchronized (mStateMonitor) {
             mSIMLockedRegistrants.add(r);
 
-            if (mState == RadioState.SIM_LOCKED_OR_ABSENT) {
+            if (mSimState == RadioState.SIM_LOCKED_OR_ABSENT) {
                 r.notifyRegistrant(new AsyncResult(null, null, null));
             }
         }
@@ -274,7 +299,7 @@ public abstract class BaseCommands implements CommandsInterface {
         synchronized (mStateMonitor) {
             mRUIMLockedRegistrants.add(r);
 
-            if (mState == RadioState.RUIM_LOCKED_OR_ABSENT) {
+            if (mRuimState == RadioState.RUIM_LOCKED_OR_ABSENT) {
                 r.notifyRegistrant(new AsyncResult(null, null, null));
             }
         }
@@ -438,16 +463,23 @@ public abstract class BaseCommands implements CommandsInterface {
         mIccSmsFullRegistrant.clear();
     }
 
+    public void registerForIccRefresh(Handler h, int what, Object obj) {
+        Registrant r = new Registrant (h, what, obj);
+        mIccRefreshRegistrants.add(r);
+    }
     public void setOnIccRefresh(Handler h, int what, Object obj) {
-        mIccRefreshRegistrant = new Registrant (h, what, obj);
+        registerForIccRefresh(h, what, obj);
     }
 
     public void setEmergencyCallbackMode(Handler h, int what, Object obj) {
         mEmergencyCallbackModeRegistrant = new Registrant (h, what, obj);
     }
 
-    public void unSetOnIccRefresh(Handler h) {
-        mIccRefreshRegistrant.clear();
+    public void unregisterForIccRefresh(Handler h) {
+        mIccRefreshRegistrants.remove(h);
+    }
+    public void unsetOnIccRefresh(Handler h) {
+        unregisterForIccRefresh(h);
     }
 
     public void setOnCallRing(Handler h, int what, Object obj) {
@@ -624,6 +656,25 @@ public abstract class BaseCommands implements CommandsInterface {
         mExitEmergencyCallbackModeRegistrants.remove(h);
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void registerForRilConnected(Handler h, int what, Object obj) {
+        Log.d(LOG_TAG, "registerForRilConnected h=" + h + " w=" + what);
+        Registrant r = new Registrant (h, what, obj);
+        mRilConnectedRegistrants.add(r);
+        if (mRilVersion != -1) {
+            Log.d(LOG_TAG, "Notifying: ril connected mRilVersion=" + mRilVersion);
+            r.notifyRegistrant(new AsyncResult(null, new Integer(mRilVersion), null));
+        }
+    }
+
+    @Override
+    public void unregisterForRilConnected(Handler h) {
+        mRilConnectedRegistrants.remove(h);
+    }
+
     //***** Protected Methods
     /**
      * Store new RadioState and send notification based on the changes
@@ -653,6 +704,22 @@ public abstract class BaseCommands implements CommandsInterface {
                 return;
             }
 
+            // FIXME: Use Constants or Enums
+            if(mState.getType() == 0) {
+                mSimState = mState;
+                mRuimState = mState;
+                mNvState = mState;
+            }
+            else if (mState.getType() == 1) {
+                mSimState = mState;
+            }
+            else if (mState.getType() == 2) {
+                mRuimState = mState;
+            }
+            else if (mState.getType() == 3) {
+                mNvState = mState;
+            }
+
             mRadioStateChangedRegistrants.notifyRegistrants();
 
             if (mState.isAvailable() && !oldState.isAvailable()) {
@@ -733,4 +800,81 @@ public abstract class BaseCommands implements CommandsInterface {
 
     protected void onRadioAvailable() {
     }
+
+    /**
+     * The contents of the /proc/cmdline file
+     */
+    private static String getProcCmdLine()
+    {
+        String cmdline = "";
+        FileInputStream is = null;
+        try {
+            is = new FileInputStream("/proc/cmdline");
+            byte [] buffer = new byte[2048];
+            int count = is.read(buffer);
+            if (count > 0) {
+                cmdline = new String(buffer, 0, count);
+            }
+        } catch (IOException e) {
+            Log.d(LOG_TAG, "No /proc/cmdline exception=" + e);
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+        Log.d(LOG_TAG, "/proc/cmdline=" + cmdline);
+        return cmdline;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getLteOnCdmaMode() {
+        return getLteOnCdmaModeStatic();
+    }
+
+    /** Kernel command line */
+    private static final String sKernelCmdLine = getProcCmdLine();
+
+    /** Pattern for selecting the product type from the kernel command line */
+    private static final Pattern sProductTypePattern =
+        Pattern.compile("\\sproduct_type\\s*=\\s*(\\w+)");
+
+    /** The ProductType used for LTE on CDMA devices */
+    private static final String sLteOnCdmaProductType =
+        SystemProperties.get(TelephonyProperties.PROPERTY_LTE_ON_CDMA_PRODUCT_TYPE, "");
+
+    /**
+     * Return if the current radio is LTE on CDMA. This
+     * is a tri-state return value as for a period of time
+     * the mode may be unknown.
+     *
+     * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
+     * or {@link Phone#LTE_ON_CDMA_TRUE}
+     */
+    public static int getLteOnCdmaModeStatic() {
+        int retVal;
+        String productType;
+
+        Matcher matcher = sProductTypePattern.matcher(sKernelCmdLine);
+        if (matcher.find()) {
+            productType = matcher.group(1);
+            if (sLteOnCdmaProductType.equals(productType)) {
+                retVal = Phone.LTE_ON_CDMA_TRUE;
+            } else {
+                retVal = Phone.LTE_ON_CDMA_FALSE;
+            }
+        } else {
+            retVal = Phone.LTE_ON_CDMA_FALSE;
+            productType = "";
+        }
+
+        Log.d(LOG_TAG, "getLteOnCdmaMode=" + retVal + " product_type='" + productType +
+                "' lteOnCdmaProductType='" + sLteOnCdmaProductType + "'");
+        return retVal;
+    }
 }
index 9619a66..31f9e18 100644 (file)
@@ -119,6 +119,10 @@ public abstract class CallTracker extends Handler {
 
     //***** Overridden from Handler
     public abstract void handleMessage (Message msg);
+    public abstract void registerForVoiceCallStarted(Handler h, int what, Object obj);
+    public abstract void unregisterForVoiceCallStarted(Handler h);
+    public abstract void registerForVoiceCallEnded(Handler h, int what, Object obj);
+    public abstract void unregisterForVoiceCallEnded(Handler h);
 
     protected abstract void log(String msg);
 
index ad21a18..b68cbe9 100644 (file)
@@ -20,6 +20,7 @@ import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
 
 import android.os.Message;
 import android.os.Handler;
+import android.os.SystemProperties;
 
 
 /**
@@ -27,18 +28,18 @@ import android.os.Handler;
  */
 public interface CommandsInterface {
     enum RadioState {
-        RADIO_OFF,         /* Radio explicitly powered off (e.g. CFUN=0) */
-        RADIO_UNAVAILABLE, /* Radio unavailable (e.g. resetting or not booted) */
-        SIM_NOT_READY,     /* Radio is on, but the SIM interface is not ready */
-        SIM_LOCKED_OR_ABSENT,  /* SIM PIN locked, PUK required, network
-                               personalization, or SIM absent */
-        SIM_READY,         /* Radio is on and SIM interface is available */
-        RUIM_NOT_READY,    /* Radio is on, but the RUIM interface is not ready */
-        RUIM_READY,        /* Radio is on and the RUIM interface is available */
-        RUIM_LOCKED_OR_ABSENT, /* RUIM PIN locked, PUK required, network
-                                  personalization locked, or RUIM absent */
-        NV_NOT_READY,      /* Radio is on, but the NV interface is not available */
-        NV_READY;          /* Radio is on and the NV interface is available */
+        RADIO_OFF(0),         /* Radio explictly powered off (eg CFUN=0) */
+        RADIO_UNAVAILABLE(0), /* Radio unavailable (eg, resetting or not booted) */
+        SIM_NOT_READY(1),     /* Radio is on, but the SIM interface is not ready */
+        SIM_LOCKED_OR_ABSENT(1),  /* SIM PIN locked, PUK required, network
+                                     personalization, or SIM absent */
+        SIM_READY(1),         /* Radio is on and SIM interface is available */
+        RUIM_NOT_READY(2),    /* Radio is on, but the RUIM interface is not ready */
+        RUIM_READY(2),        /* Radio is on and the RUIM interface is available */
+        RUIM_LOCKED_OR_ABSENT(2), /* RUIM PIN locked, PUK required, network
+                                     personalization locked, or RUIM absent */
+        NV_NOT_READY(3),      /* Radio is on, but the NV interface is not available */
+        NV_READY(3);          /* Radio is on and the NV interface is available */
 
         public boolean isOn() /* and available...*/ {
             return this == SIM_NOT_READY
@@ -50,6 +51,14 @@ public interface CommandsInterface {
                     || this == NV_NOT_READY
                     || this == NV_READY;
         }
+        private int stateType;
+        private RadioState (int type) {
+            stateType = type;
+        }
+
+        public int getType() {
+            return stateType;
+        }
 
         public boolean isAvailable() {
             return this != RADIO_UNAVAILABLE;
@@ -68,17 +77,25 @@ public interface CommandsInterface {
         }
 
         public boolean isGsm() {
-            return this == SIM_NOT_READY
-                    || this == SIM_LOCKED_OR_ABSENT
-                    || this == SIM_READY;
+            if (BaseCommands.getLteOnCdmaModeStatic() == Phone.LTE_ON_CDMA_TRUE) {
+                return false;
+            } else {
+                return this == SIM_NOT_READY
+                        || this == SIM_LOCKED_OR_ABSENT
+                        || this == SIM_READY;
+            }
         }
 
         public boolean isCdma() {
-            return this ==  RUIM_NOT_READY
-                    || this == RUIM_READY
-                    || this == RUIM_LOCKED_OR_ABSENT
-                    || this == NV_NOT_READY
-                    || this == NV_READY;
+            if (BaseCommands.getLteOnCdmaModeStatic() == Phone.LTE_ON_CDMA_TRUE) {
+                return true;
+            } else {
+                return this ==  RUIM_NOT_READY
+                        || this == RUIM_READY
+                        || this == RUIM_LOCKED_OR_ABSENT
+                        || this == NV_NOT_READY
+                        || this == NV_READY;
+            }
         }
     }
 
@@ -153,6 +170,9 @@ public interface CommandsInterface {
     //***** Methods
 
     RadioState getRadioState();
+    RadioState getSimState();
+    RadioState getRuimState();
+    RadioState getNvState();
 
     /**
      * Fires on any RadioState transition
@@ -214,6 +234,9 @@ public interface CommandsInterface {
     void registerForSIMLockedOrAbsent(Handler h, int what, Object obj);
     void unregisterForSIMLockedOrAbsent(Handler h);
 
+    void registerForIccStatusChanged(Handler h, int what, Object obj);
+    void unregisterForIccStatusChanged(Handler h);
+
     void registerForCallStateChanged(Handler h, int what, Object obj);
     void unregisterForCallStateChanged(Handler h);
     void registerForVoiceNetworkStateChanged(Handler h, int what, Object obj);
@@ -330,14 +353,16 @@ public interface CommandsInterface {
 
     /**
      * Sets the handler for SIM Refresh notifications.
-     * Unlike the register* methods, there's only one notification handler
      *
      * @param h Handler for notification message.
      * @param what User-defined message code.
      * @param obj User object.
      */
+    void registerForIccRefresh(Handler h, int what, Object obj);
+    void unregisterForIccRefresh(Handler h);
+
     void setOnIccRefresh(Handler h, int what, Object obj);
-    void unSetOnIccRefresh(Handler h);
+    void unsetOnIccRefresh(Handler h);
 
     /**
      * Sets the handler for RING notifications.
@@ -582,6 +607,20 @@ public interface CommandsInterface {
      void registerForExitEmergencyCallbackMode(Handler h, int what, Object obj);
      void unregisterForExitEmergencyCallbackMode(Handler h);
 
+     /**
+      * Registers the handler for RIL_UNSOL_RIL_CONNECT events.
+      *
+      * When ril connects or disconnects a message is sent to the registrant
+      * which contains an AsyncResult, ar, in msg.obj. The ar.result is an
+      * Integer which is the version of the ril or -1 if the ril disconnected.
+      *
+      * @param h Handler for notification message.
+      * @param what User-defined message code.
+      * @param obj User object.
+      */
+     void registerForRilConnected(Handler h, int what, Object obj);
+     void unregisterForRilConnected(Handler h);
+
     /**
      * Supply the ICC PIN to the ICC card
      *
@@ -1419,6 +1458,12 @@ public interface CommandsInterface {
     void setCdmaSubscriptionSource(int cdmaSubscriptionType, Message response);
 
     /**
+     *  Requests to get the CDMA subscription srouce
+     * @param response is callback message
+     */
+    void getCdmaSubscriptionSource(Message response);
+
+    /**
      *  Set the TTY mode
      *
      * @param ttyMode one of the following:
@@ -1528,4 +1573,14 @@ public interface CommandsInterface {
      *          Callback message containing {@link IccCardStatus} structure for the card.
      */
     public void getIccCardStatus(Message result);
+
+    /**
+     * Return if the current radio is LTE on CDMA. This
+     * is a tri-state return value as for a period of time
+     * the mode may be unknown.
+     *
+     * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
+     * or {@link Phone#LTE_ON_CDMA_TRUE}
+     */
+    public int getLteOnCdmaMode();
 }
index fda1e47..fba3184 100644 (file)
 
 package com.android.internal.telephony;
 
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.NetworkUtils;
+import android.net.RouteInfo;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import com.android.internal.telephony.DataConnection.FailCause;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
 /**
  * This is RIL_Data_Call_Response_v5 from ril.h
  * TODO: Rename to DataCallResponse.
  */
 public class DataCallState {
+    private final boolean DBG = true;
+    private final String LOG_TAG = "GSM";
+
     public int version = 0;
     public int status = 0;
     public int cid = 0;
@@ -31,6 +47,30 @@ public class DataCallState {
     public String [] addresses = new String[0];
     public String [] dnses = new String[0];
     public String[] gateways = new String[0];
+    public int suggestedRetryTime = -1;
+
+    /**
+     * Class returned by onSetupConnectionCompleted.
+     */
+    protected enum SetupResult {
+        SUCCESS,
+        ERR_BadCommand,
+        ERR_UnacceptableParameter,
+        ERR_GetLastErrorFromRil,
+        ERR_Stale,
+        ERR_RilError;
+
+        public FailCause mFailCause;
+
+        SetupResult() {
+            mFailCause = FailCause.fromInt(0);
+        }
+
+        @Override
+        public String toString() {
+            return name() + "  SetupResult.mFailCause=" + mFailCause;
+        }
+    }
 
     @Override
     public String toString() {
@@ -38,6 +78,7 @@ public class DataCallState {
         sb.append("DataCallState: {")
            .append("version=").append(version)
            .append(" status=").append(status)
+           .append(" retry=").append(suggestedRetryTime)
            .append(" cid=").append(cid)
            .append(" active=").append(active)
            .append(" type=").append(type)
@@ -63,4 +104,127 @@ public class DataCallState {
         sb.append("]}");
         return sb.toString();
     }
+
+    public SetupResult setLinkProperties(LinkProperties linkProperties,
+            boolean okToUseSystemPropertyDns) {
+        SetupResult result;
+
+        // Start with clean network properties and if we have
+        // a failure we'll clear again at the bottom of this code.
+        if (linkProperties == null)
+            linkProperties = new LinkProperties();
+        else
+            linkProperties.clear();
+
+        if (status == FailCause.NONE.getErrorCode()) {
+            String propertyPrefix = "net." + ifname + ".";
+
+            try {
+                // set interface name
+                linkProperties.setInterfaceName(ifname);
+
+                // set link addresses
+                if (addresses != null && addresses.length > 0) {
+                    for (String addr : addresses) {
+                        LinkAddress la;
+                        int addrPrefixLen;
+
+                        String [] ap = addr.split("/");
+                        if (ap.length == 2) {
+                            addr = ap[0];
+                            addrPrefixLen = Integer.parseInt(ap[1]);
+                        } else {
+                            addrPrefixLen = 0;
+                        }
+                        InetAddress ia;
+                        try {
+                            ia = NetworkUtils.numericToInetAddress(addr);
+                        } catch (IllegalArgumentException e) {
+                            throw new UnknownHostException("Non-numeric ip addr=" + addr);
+                        }
+                        if (addrPrefixLen == 0) {
+                            // Assume point to point
+                            addrPrefixLen = (ia instanceof Inet4Address) ? 32 : 128;
+                        }
+                        if (DBG) Log.d(LOG_TAG, "addr/pl=" + addr + "/" + addrPrefixLen);
+                        la = new LinkAddress(ia, addrPrefixLen);
+                        linkProperties.addLinkAddress(la);
+                    }
+                } else {
+                    throw new UnknownHostException("no address for ifname=" + ifname);
+                }
+
+                // set dns servers
+                if (dnses != null && dnses.length > 0) {
+                    for (String addr : dnses) {
+                        InetAddress ia;
+                        try {
+                            ia = NetworkUtils.numericToInetAddress(addr);
+                        } catch (IllegalArgumentException e) {
+                            throw new UnknownHostException("Non-numeric dns addr=" + addr);
+                        }
+                        linkProperties.addDns(ia);
+                    }
+                } else if (okToUseSystemPropertyDns){
+                    String dnsServers[] = new String[2];
+                    dnsServers[0] = SystemProperties.get(propertyPrefix + "dns1");
+                    dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2");
+                    for (String dnsAddr : dnsServers) {
+                            InetAddress ia;
+                            try {
+                                ia = NetworkUtils.numericToInetAddress(dnsAddr);
+                            } catch (IllegalArgumentException e) {
+                                throw new UnknownHostException("Non-numeric dns addr="
+                                            + dnsAddr);
+                            }
+                            linkProperties.addDns(ia);
+                    }
+                } else {
+                    throw new UnknownHostException("Empty dns response and no system default dns");
+                }
+
+                // set gateways
+                if ((gateways == null) || (gateways.length == 0)) {
+                    String sysGateways = SystemProperties.get(propertyPrefix + "gw");
+                    if (sysGateways != null) {
+                        gateways = sysGateways.split(" ");
+                    } else {
+                        gateways = new String[0];
+                    }
+                }
+                for (String addr : gateways) {
+                    InetAddress ia;
+                    try {
+                        ia = NetworkUtils.numericToInetAddress(addr);
+                    } catch (IllegalArgumentException e) {
+                        throw new UnknownHostException("Non-numeric gateway addr=" + addr);
+                    }
+                    linkProperties.addRoute(new RouteInfo(ia));
+                }
+
+                result = SetupResult.SUCCESS;
+            } catch (UnknownHostException e) {
+                Log.d(LOG_TAG, "setLinkProperties: UnknownHostException " + e);
+                e.printStackTrace();
+                result = SetupResult.ERR_UnacceptableParameter;
+            }
+        } else {
+            if (version < 4) {
+                result = SetupResult.ERR_GetLastErrorFromRil;
+            } else {
+                result = SetupResult.ERR_RilError;
+            }
+        }
+
+        // An error occurred so clear properties
+        if (result != SetupResult.SUCCESS) {
+            if(DBG) {
+                Log.d(LOG_TAG, "setLinkProperties: error clearing LinkProperties " +
+                        "status=" + status + " result=" + result);
+            }
+            linkProperties.clear();
+        }
+
+        return result;
+    }
 }
index 89513fd..5c84fdc 100644 (file)
 package com.android.internal.telephony;
 
 
-import com.android.internal.util.HierarchicalState;
-import com.android.internal.util.HierarchicalStateMachine;
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
 
+import android.app.PendingIntent;
 import android.net.LinkAddress;
 import android.net.LinkCapabilities;
 import android.net.LinkProperties;
 import android.net.NetworkUtils;
+import android.net.ProxyProperties;
 import android.os.AsyncResult;
+import android.os.Bundle;
 import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.SystemProperties;
 import android.text.TextUtils;
 
-import java.net.InetAddress;
-import java.net.Inet4Address;
-import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * {@hide}
  *
- * DataConnection HierarchicalStateMachine.
+ * DataConnection StateMachine.
  *
  * This is an abstract base class for representing a single data connection.
  * Instances of this class such as <code>CdmaDataConnection</code> and
@@ -55,34 +63,16 @@ import java.util.HashMap;
  *
  * The other public methods are provided for debugging.
  */
-public abstract class DataConnection extends HierarchicalStateMachine {
+public abstract class DataConnection extends StateMachine {
     protected static final boolean DBG = true;
+    protected static final boolean VDBG = false;
 
     protected static Object mCountLock = new Object();
     protected static int mCount;
+    protected AsyncChannel mAc;
 
-    /**
-     * Class returned by onSetupConnectionCompleted.
-     */
-    protected enum SetupResult {
-        SUCCESS,
-        ERR_BadCommand,
-        ERR_UnacceptableParameter,
-        ERR_GetLastErrorFromRil,
-        ERR_Stale,
-        ERR_RilError;
-
-        public FailCause mFailCause;
-
-        SetupResult() {
-            mFailCause = FailCause.fromInt(0);
-        }
-
-        @Override
-        public String toString() {
-            return name() + "  SetupResult.mFailCause=" + mFailCause;
-        }
-    }
+    private List<ApnContext> mApnList = null;
+    PendingIntent mReconnectIntent = null;
 
     /**
      * Used internally for saving connecting parameters.
@@ -99,26 +89,16 @@ public abstract class DataConnection extends HierarchicalStateMachine {
     }
 
     /**
-     * An instance used for notification of blockingReset.
-     * TODO: Remove when blockingReset is removed.
-     */
-    class ResetSynchronouslyLock {
-    }
-
-    /**
      * Used internally for saving disconnecting parameters.
      */
     protected static class DisconnectParams {
-        public DisconnectParams(Message onCompletedMsg) {
+        public DisconnectParams(String reason, Message onCompletedMsg) {
+            this.reason = reason;
             this.onCompletedMsg = onCompletedMsg;
         }
-        public DisconnectParams(ResetSynchronouslyLock lockObj) {
-            this.lockObj = lockObj;
-        }
-
         public int tag;
+        public String reason;
         public Message onCompletedMsg;
-        public ResetSynchronouslyLock lockObj;
     }
 
     /**
@@ -167,7 +147,7 @@ public abstract class DataConnection extends HierarchicalStateMachine {
         static {
             sErrorCodeToFailCauseMap = new HashMap<Integer, FailCause>();
             for (FailCause fc : values()) {
-                sErrorCodeToFailCauseMap.put(fc.ordinal(), fc);
+                sErrorCodeToFailCauseMap.put(fc.getErrorCode(), fc);
             }
         }
 
@@ -207,29 +187,44 @@ public abstract class DataConnection extends HierarchicalStateMachine {
         }
     }
 
+    public static class CallSetupException extends Exception {
+        private int mRetryOverride = -1;
+
+        CallSetupException (int retryOverride) {
+            mRetryOverride = retryOverride;
+        }
+
+        public int getRetryOverride() {
+            return mRetryOverride;
+        }
+    }
+
     // ***** Event codes for driving the state machine
-    protected static final int EVENT_RESET = 1;
-    protected static final int EVENT_CONNECT = 2;
-    protected static final int EVENT_SETUP_DATA_CONNECTION_DONE = 3;
-    protected static final int EVENT_GET_LAST_FAIL_DONE = 4;
-    protected static final int EVENT_DEACTIVATE_DONE = 5;
-    protected static final int EVENT_DISCONNECT = 6;
+    protected static final int BASE = Protocol.BASE_DATA_CONNECTION;
+    protected static final int EVENT_CONNECT = BASE + 0;
+    protected static final int EVENT_SETUP_DATA_CONNECTION_DONE = BASE + 1;
+    protected static final int EVENT_GET_LAST_FAIL_DONE = BASE + 2;
+    protected static final int EVENT_DEACTIVATE_DONE = BASE + 3;
+    protected static final int EVENT_DISCONNECT = BASE + 4;
+    protected static final int EVENT_RIL_CONNECTED = BASE + 5;
 
     //***** Tag IDs for EventLog
     protected static final int EVENT_LOG_BAD_DNS_ADDRESS = 50100;
 
     //***** Member Variables
-    protected int mId;
+    protected ApnSetting mApn;
     protected int mTag;
     protected PhoneBase phone;
-    protected RetryManager mRetryMgr;
+    protected int mRilVersion = -1;
     protected int cid;
     protected LinkProperties mLinkProperties = new LinkProperties();
     protected LinkCapabilities mCapabilities = new LinkCapabilities();
     protected long createTime;
     protected long lastFailTime;
     protected FailCause lastFailCause;
+    protected int mRetryOverride = -1;
     protected static final String NULL_IP = "0.0.0.0";
+    private int mRefCount;
     Object userData;
 
     //***** Abstract methods
@@ -244,13 +239,13 @@ public abstract class DataConnection extends HierarchicalStateMachine {
 
 
    //***** Constructor
-    protected DataConnection(PhoneBase phone, String name, RetryManager rm) {
+    protected DataConnection(PhoneBase phone, String name, int id, RetryManager rm) {
         super(name);
         if (DBG) log("DataConnection constructor E");
         this.phone = phone;
+        mId = id;
         mRetryMgr = rm;
         this.cid = -1;
-        clearSettings();
 
         setDbg(false);
         addState(mDefaultState);
@@ -260,6 +255,8 @@ public abstract class DataConnection extends HierarchicalStateMachine {
             addState(mDisconnectingState, mDefaultState);
             addState(mDisconnectingErrorCreatingConnection, mDefaultState);
         setInitialState(mInactiveState);
+
+        mApnList = new ArrayList<ApnContext>();
         if (DBG) log("DataConnection constructor X");
     }
 
@@ -274,10 +271,10 @@ public abstract class DataConnection extends HierarchicalStateMachine {
         if ((o != null) && (o instanceof DisconnectParams)) {
             DisconnectParams dp = (DisconnectParams)o;
             Message m = dp.onCompletedMsg;
-            if ((m != null) && (m.obj != null) && (m.obj instanceof String)) {
-                String reason = (String)m.obj;
-                if (TextUtils.equals(reason, Phone.REASON_RADIO_TURNED_OFF))
-                    discReason = RILConstants.DEACTIVATE_REASON_RADIO_OFF;
+            if (TextUtils.equals(dp.reason, Phone.REASON_RADIO_TURNED_OFF)) {
+                discReason = RILConstants.DEACTIVATE_REASON_RADIO_OFF;
+            } else if (TextUtils.equals(dp.reason, Phone.REASON_PDP_RESET)) {
+                discReason = RILConstants.DEACTIVATE_REASON_PDP_RESET;
             }
         }
         if (phone.mCM.getRadioState().isOn()) {
@@ -311,9 +308,10 @@ public abstract class DataConnection extends HierarchicalStateMachine {
         } else {
             lastFailCause = cause;
             lastFailTime = timeStamp;
-            AsyncResult.forMessage(connectionCompletedMsg, cause, new Exception());
+            AsyncResult.forMessage(connectionCompletedMsg, cause,
+                                   new CallSetupException(mRetryOverride));
         }
-        if (DBG) log("notifyConnection at " + timeStamp + " cause=" + cause);
+        if (DBG) log("notifyConnectionCompleted at " + timeStamp + " cause=" + cause);
 
         connectionCompletedMsg.sendToTarget();
     }
@@ -324,39 +322,140 @@ public abstract class DataConnection extends HierarchicalStateMachine {
      * @param dp is the DisconnectParams.
      */
     private void notifyDisconnectCompleted(DisconnectParams dp) {
-        if (DBG) log("NotifyDisconnectCompleted");
+        if (VDBG) log("NotifyDisconnectCompleted");
 
         if (dp.onCompletedMsg != null) {
             Message msg = dp.onCompletedMsg;
-            log(String.format("msg=%s msg.obj=%s", msg.toString(),
+            if (VDBG) {
+                log(String.format("msg=%s msg.obj=%s", msg.toString(),
                     ((msg.obj instanceof String) ? (String) msg.obj : "<no-reason>")));
+            }
             AsyncResult.forMessage(msg);
             msg.sendToTarget();
         }
-        if (dp.lockObj != null) {
-            synchronized(dp.lockObj) {
-                dp.lockObj.notify();
-            }
+        if (DBG) log("NotifyDisconnectCompleted DisconnectParams=" + dp);
+    }
+
+    protected int getRadioTechnology(int defaultRadioTechnology) {
+        int radioTechnology;
+        if (mRilVersion < 6) {
+            radioTechnology = defaultRadioTechnology;
+        } else {
+            radioTechnology = phone.getServiceState().getRadioTechnology() + 2;
         }
+        return radioTechnology;
+    }
+
+    /*
+     * **************************************************************************
+     * Begin Members and methods owned by DataConnectionTracker but stored
+     * in a DataConnection because there is one per connection.
+     * **************************************************************************
+     */
+
+    /*
+     * The id is owned by DataConnectionTracker.
+     */
+    private int mId;
 
-        clearSettings();
+    /**
+     * Get the DataConnection ID
+     */
+    public int getDataConnectionId() {
+        return mId;
+    }
+
+    /*
+     * The retry manager is currently owned by the DataConnectionTracker but is stored
+     * in the DataConnection because there is one per connection. These methods
+     * should only be used by the DataConnectionTracker although someday the retrying
+     * maybe managed by the DataConnection itself and these methods could disappear.
+     */
+    private RetryManager mRetryMgr;
+
+    /**
+     * @return retry manager retryCount
+     */
+    public int getRetryCount() {
+        return mRetryMgr.getRetryCount();
+    }
+
+    /**
+     * @return retry manager retryTimer
+     */
+    public int getRetryTimer() {
+        return mRetryMgr.getRetryTimer();
+    }
+
+    /**
+     * increaseRetryCount of retry manager
+     */
+    public void increaseRetryCount() {
+        mRetryMgr.increaseRetryCount();
+    }
+
+    /**
+     * @return retry manager isRetryNeeded
+     */
+    public boolean isRetryNeeded() {
+        return mRetryMgr.isRetryNeeded();
+    }
+
+    /**
+     * resetRetryCount of retry manager
+     */
+    public void resetRetryCount() {
+        mRetryMgr.resetRetryCount();
     }
 
-    public RetryManager getRetryMgr() {
-        return mRetryMgr;
+    /**
+     * set retryForeverUsingLasttimeout of retry manager
+     */
+    public void retryForeverUsingLastTimeout() {
+        mRetryMgr.retryForeverUsingLastTimeout();
     }
 
     /**
+     * @return retry manager isRetryForever
+     */
+    public boolean isRetryForever() {
+        return mRetryMgr.isRetryForever();
+    }
+
+    /**
+     * @return whether the retry config is set successfully or not
+     */
+    public boolean configureRetry(int maxRetryCount, int retryTime, int randomizationTime) {
+        return mRetryMgr.configure(maxRetryCount, retryTime, randomizationTime);
+    }
+
+    /**
+     * @return whether the retry config is set successfully or not
+     */
+    public boolean configureRetry(String configStr) {
+        return mRetryMgr.configure(configStr);
+    }
+
+    /*
+     * **************************************************************************
+     * End members owned by DataConnectionTracker
+     * **************************************************************************
+     */
+
+    /**
      * Clear all settings called when entering mInactiveState.
      */
     protected void clearSettings() {
         if (DBG) log("clearSettings");
 
-        this.createTime = -1;
-        this.lastFailTime = -1;
-        this.lastFailCause = FailCause.NONE;
+        createTime = -1;
+        lastFailTime = -1;
+        lastFailCause = FailCause.NONE;
+        mRetryOverride = -1;
+        mRefCount = 0;
 
         mLinkProperties = new LinkProperties();
+        mApn = null;
     }
 
     /**
@@ -365,10 +464,10 @@ public abstract class DataConnection extends HierarchicalStateMachine {
      * @param ar is the result
      * @return SetupResult.
      */
-    private SetupResult onSetupConnectionCompleted(AsyncResult ar) {
+    private DataCallState.SetupResult onSetupConnectionCompleted(AsyncResult ar) {
         DataCallState response = (DataCallState) ar.result;
         ConnectionParams cp = (ConnectionParams) ar.userObj;
-        SetupResult result;
+        DataCallState.SetupResult result;
 
         if (ar.exception != null) {
             if (DBG) {
@@ -379,169 +478,232 @@ public abstract class DataConnection extends HierarchicalStateMachine {
             if (ar.exception instanceof CommandException
                     && ((CommandException) (ar.exception)).getCommandError()
                     == CommandException.Error.RADIO_NOT_AVAILABLE) {
-                result = SetupResult.ERR_BadCommand;
+                result = DataCallState.SetupResult.ERR_BadCommand;
                 result.mFailCause = FailCause.RADIO_NOT_AVAILABLE;
             } else if ((response == null) || (response.version < 4)) {
-                result = SetupResult.ERR_GetLastErrorFromRil;
+                result = DataCallState.SetupResult.ERR_GetLastErrorFromRil;
             } else {
-                result = SetupResult.ERR_RilError;
+                result = DataCallState.SetupResult.ERR_RilError;
                 result.mFailCause = FailCause.fromInt(response.status);
             }
         } else if (cp.tag != mTag) {
             if (DBG) {
                 log("BUG: onSetupConnectionCompleted is stale cp.tag=" + cp.tag + ", mtag=" + mTag);
             }
-            result = SetupResult.ERR_Stale;
+            result = DataCallState.SetupResult.ERR_Stale;
+        } else if (response.status != 0) {
+            result = DataCallState.SetupResult.ERR_RilError;
+            result.mFailCause = FailCause.fromInt(response.status);
         } else {
-            log("onSetupConnectionCompleted received DataCallState: " + response);
-
-            // Start with clean network properties and if we have
-            // a failure we'll clear again at the bottom of this code.
-            LinkProperties linkProperties = new LinkProperties();
-            if (response.status == FailCause.NONE.getErrorCode()) {
-                String propertyPrefix = "net." + response.ifname + ".";
-
-                try {
-                    cid = response.cid;
-                    linkProperties.setInterfaceName(response.ifname);
-                    if (response.addresses != null && response.addresses.length > 0) {
-                        for (String addr : response.addresses) {
-                            LinkAddress la;
-                            int addrPrefixLen;
-
-                            String [] ap = addr.split("/");
-                            if (ap.length == 2) {
-                                addr = ap[0];
-                                addrPrefixLen = Integer.parseInt(ap[1]);
-                            } else {
-                                addrPrefixLen = 0;
-                            }
-                            InetAddress ia;
-                            try {
-                                ia = NetworkUtils.numericToInetAddress(addr);
-                            } catch (IllegalArgumentException e) {
-                                EventLogTags.writeBadIpAddress(addr);
-                                throw new UnknownHostException("Non-numeric ip addr=" + addr);
-                            }
-                            if (addrPrefixLen == 0) {
-                                // Assume point to point
-                                addrPrefixLen = (ia instanceof Inet4Address) ? 32 : 128;
-                            }
-                            if (DBG) log("addr/pl=" + addr + "/" + addrPrefixLen);
-                            la = new LinkAddress(ia, addrPrefixLen);
-                            linkProperties.addLinkAddress(la);
-                        }
-                    } else {
-                        EventLogTags.writeBadIpAddress("no address for ifname=" + response.ifname);
-                        throw new UnknownHostException("no address for ifname=" + response.ifname);
-                    }
-                    if (response.dnses != null && response.dnses.length > 0) {
-                        for (String addr : response.dnses) {
-                            InetAddress ia;
-                            try {
-                                ia = NetworkUtils.numericToInetAddress(addr);
-                            } catch (IllegalArgumentException e) {
-                                EventLogTags.writePdpBadDnsAddress("dns=" + addr); 
-                                throw new UnknownHostException("Non-numeric dns addr=" + addr);
-                            }
-                            linkProperties.addDns(ia);
-                        }
-                        result = SetupResult.SUCCESS;
-                    } else {
-                        String dnsServers[] = new String[2];
-                        dnsServers[0] = SystemProperties.get(propertyPrefix + "dns1");
-                        dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2");
-                        if (isDnsOk(dnsServers)) {
-                            for (String dnsAddr : dnsServers) {
-                                InetAddress ia;
-                                try {
-                                    ia = NetworkUtils.numericToInetAddress(dnsAddr);
-                                } catch (IllegalArgumentException e) {
-                                    EventLogTags.writePdpBadDnsAddress("dnsAddr=" + dnsAddr);
-                                    throw new UnknownHostException("Non-numeric dns addr="
-                                                + dnsAddr);
-                                }
-                                linkProperties.addDns(ia);
-                            }
-                            result = SetupResult.SUCCESS;
-                        } else {
-                            StringBuilder sb = new StringBuilder();
-                            for (String dnsAddr : dnsServers) {
-                                sb.append(dnsAddr);
-                                sb.append(" ");
-                            }
-                            EventLogTags.writePdpBadDnsAddress("Unacceptable dns addresses=" + sb);
-                            throw new UnknownHostException("Unacceptable dns addresses=" + sb);
-                        }
-                    }
-                    if ((response.gateways == null) || (response.gateways.length == 0)) {
-                        String gateways = SystemProperties.get(propertyPrefix + "gw");
-                        if (gateways != null) {
-                            response.gateways = gateways.split(" ");
-                        } else {
-                            response.gateways = new String[0];
-                        }
-                    }
-                    for (String addr : response.gateways) {
-                        InetAddress ia;
-                        try {
-                            ia = NetworkUtils.numericToInetAddress(addr);
-                        } catch (IllegalArgumentException e) {
-                            EventLogTags.writePdpBadDnsAddress("gateway=" + addr);
-                            throw new UnknownHostException("Non-numeric gateway addr=" + addr);
-                        }
-                        linkProperties.addGateway(ia);
-                    }
-                    result = SetupResult.SUCCESS;
-                } catch (UnknownHostException e) {
-                    log("onSetupCompleted: UnknownHostException " + e);
-                    e.printStackTrace();
-                    result = SetupResult.ERR_UnacceptableParameter;
-                }
-            } else {
-                if (response.version < 4) {
-                    result = SetupResult.ERR_GetLastErrorFromRil;
-                } else {
-                    result = SetupResult.ERR_RilError;
-                }
-            }
+            if (DBG) log("onSetupConnectionCompleted received DataCallState: " + response);
+            cid = response.cid;
+            // set link properties based on data call response
+            result = setLinkProperties(response, mLinkProperties);
+        }
 
-            // An error occurred so clear properties
-            if (result != SetupResult.SUCCESS) {
-                log("onSetupConnectionCompleted with an error, clearing LinkProperties");
-                linkProperties.clear();
-            }
-            mLinkProperties = linkProperties;
+        return result;
+    }
+
+    private int getSuggestedRetryTime(AsyncResult ar) {
+        int retry = -1;
+        if (ar.exception == null) {
+            DataCallState response = (DataCallState) ar.result;
+            retry =  response.suggestedRetryTime;
         }
+        return retry;
+    }
 
-        if (DBG) {
-            log("onSetupConnectionCompleted: DataConnection setup result='"
-                    + result + "' on cid=" + cid);
-            if (result == SetupResult.SUCCESS) {
-                log("onSetupConnectionCompleted: LinkProperties: " + mLinkProperties.toString());
+    private DataCallState.SetupResult setLinkProperties(DataCallState response,
+            LinkProperties lp) {
+        // Check if system property dns usable
+        boolean okToUseSystemPropertyDns = false;
+        String propertyPrefix = "net." + response.ifname + ".";
+        String dnsServers[] = new String[2];
+        dnsServers[0] = SystemProperties.get(propertyPrefix + "dns1");
+        dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2");
+        okToUseSystemPropertyDns = isDnsOk(dnsServers);
+
+        // set link properties based on data call response
+        return response.setLinkProperties(lp, okToUseSystemPropertyDns);
+    }
+
+    private DataConnectionAc.LinkPropertyChangeAction updateLinkProperty(
+                                                      DataCallState newState) {
+        DataConnectionAc.LinkPropertyChangeAction changed =
+                        DataConnectionAc.LinkPropertyChangeAction.NONE;
+
+        if (newState == null) return changed;
+
+        DataCallState.SetupResult result;
+        LinkProperties newLp = new LinkProperties();
+
+        // set link properties based on data call response
+        result = setLinkProperties(newState, newLp);
+        if (result != DataCallState.SetupResult.SUCCESS) {
+            if (DBG) log("UpdateLinkProperty failed : " + result);
+            return changed;
+        }
+        // copy HTTP proxy as it is not part DataCallState.
+        newLp.setHttpProxy(mLinkProperties.getHttpProxy());
+
+        if (DBG) log("old LP=" + mLinkProperties);
+        if (DBG) log("new LP=" + newLp);
+
+        // Check consistency of link address. Currently we expect
+        // only one "global" address is assigned per each IP type.
+        Collection<LinkAddress> oLinks = mLinkProperties.getLinkAddresses();
+        Collection<LinkAddress> nLinks = newLp.getLinkAddresses();
+        for (LinkAddress oldLink : oLinks) {
+            for (LinkAddress newLink : nLinks) {
+                if ((NetworkUtils.addressTypeMatches(oldLink.getAddress(),
+                                        newLink.getAddress())) &&
+                    (oldLink.equals(newLink) == false)) {
+                    return DataConnectionAc.LinkPropertyChangeAction.RESET;
+                }
             }
         }
-        return result;
+
+        if (mLinkProperties == null || !mLinkProperties.equals(newLp)) {
+            mLinkProperties = newLp;
+            changed = DataConnectionAc.LinkPropertyChangeAction.CHANGED;
+        }
+
+        return changed;
     }
 
     /**
      * The parent state for all other states.
      */
-    private class DcDefaultState extends HierarchicalState {
+    private class DcDefaultState extends State {
+        @Override
+        public void enter() {
+            phone.mCM.registerForRilConnected(getHandler(), EVENT_RIL_CONNECTED, null);
+        }
+        @Override
+        public void exit() {
+            phone.mCM.unregisterForRilConnected(getHandler());
+        }
         @Override
-        protected boolean processMessage(Message msg) {
+        public boolean processMessage(Message msg) {
             AsyncResult ar;
 
             switch (msg.what) {
-                case EVENT_RESET:
-                    if (DBG) log("DcDefaultState: msg.what=EVENT_RESET");
-                    clearSettings();
-                    if (msg.obj != null) {
-                        notifyDisconnectCompleted((DisconnectParams) msg.obj);
+                case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
+                    if (mAc != null) {
+                        if (VDBG) log("Disconnecting to previous connection mAc=" + mAc);
+                        mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
+                                AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED);
+                    } else {
+                        mAc = new AsyncChannel();
+                        mAc.connected(null, getHandler(), msg.replyTo);
+                        if (VDBG) log("DcDefaultState: FULL_CONNECTION reply connected");
+                        mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
+                                AsyncChannel.STATUS_SUCCESSFUL, mId, "hi");
                     }
+                    break;
+                }
+                case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
+                    if (VDBG) log("CMD_CHANNEL_DISCONNECT");
+                    mAc.disconnect();
+                    break;
+                }
+                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+                    if (VDBG) log("CMD_CHANNEL_DISCONNECTED");
+                    mAc = null;
+                    break;
+                }
+                case DataConnectionAc.REQ_IS_INACTIVE: {
+                    boolean val = getCurrentState() == mInactiveState;
+                    if (VDBG) log("REQ_IS_INACTIVE  isInactive=" + val);
+                    mAc.replyToMessage(msg, DataConnectionAc.RSP_IS_INACTIVE, val ? 1 : 0);
+                    break;
+                }
+                case DataConnectionAc.REQ_GET_CID: {
+                    if (VDBG) log("REQ_GET_CID  cid=" + cid);
+                    mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_CID, cid);
+                    break;
+                }
+                case DataConnectionAc.REQ_GET_APNSETTING: {
+                    if (VDBG) log("REQ_GET_APNSETTING  apnSetting=" + mApn);
+                    mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_APNSETTING, mApn);
+                    break;
+                }
+                case DataConnectionAc.REQ_GET_LINK_PROPERTIES: {
+                    LinkProperties lp = new LinkProperties(mLinkProperties);
+                    if (VDBG) log("REQ_GET_LINK_PROPERTIES linkProperties" + lp);
+                    mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_LINK_PROPERTIES, lp);
+                    break;
+                }
+                case DataConnectionAc.REQ_SET_LINK_PROPERTIES_HTTP_PROXY: {
+                    ProxyProperties proxy = (ProxyProperties) msg.obj;
+                    if (VDBG) log("REQ_SET_LINK_PROPERTIES_HTTP_PROXY proxy=" + proxy);
+                    mLinkProperties.setHttpProxy(proxy);
+                    mAc.replyToMessage(msg, DataConnectionAc.RSP_SET_LINK_PROPERTIES_HTTP_PROXY);
+                    break;
+                }
+                case DataConnectionAc.REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE: {
+                    DataCallState newState = (DataCallState) msg.obj;
+                    DataConnectionAc.LinkPropertyChangeAction action = updateLinkProperty(newState);
+                    if (VDBG) {
+                        log("REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE action="
+                            + action + " newState=" + newState);
+                    }
+                    mAc.replyToMessage(msg,
+                                   DataConnectionAc.RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE,
+                                   action.ordinal());
+                    break;
+                }
+                case DataConnectionAc.REQ_GET_LINK_CAPABILITIES: {
+                    LinkCapabilities lc = new LinkCapabilities(mCapabilities);
+                    if (VDBG) log("REQ_GET_LINK_CAPABILITIES linkCapabilities" + lc);
+                    mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_LINK_CAPABILITIES, lc);
+                    break;
+                }
+                case DataConnectionAc.REQ_RESET:
+                    if (VDBG) log("DcDefaultState: msg.what=REQ_RESET");
+                    mAc.replyToMessage(msg, DataConnectionAc.RSP_RESET);
                     transitionTo(mInactiveState);
                     break;
-
+                case DataConnectionAc.REQ_GET_REFCOUNT: {
+                    if (VDBG) log("REQ_GET_REFCOUNT  refCount=" + mRefCount);
+                    mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_REFCOUNT, mRefCount);
+                    break;
+                }
+                case DataConnectionAc.REQ_ADD_APNCONTEXT: {
+                    ApnContext apnContext = (ApnContext) msg.obj;
+                    if (VDBG) log("REQ_ADD_APNCONTEXT apn=" + apnContext.getApnType());
+                    if (!mApnList.contains(apnContext)) {
+                        mApnList.add(apnContext);
+                    }
+                    mAc.replyToMessage(msg, DataConnectionAc.RSP_ADD_APNCONTEXT);
+                    break;
+                }
+                case DataConnectionAc.REQ_REMOVE_APNCONTEXT: {
+                    ApnContext apnContext = (ApnContext) msg.obj;
+                    if (VDBG) log("REQ_REMOVE_APNCONTEXT apn=" + apnContext.getApnType());
+                    mApnList.remove(apnContext);
+                    mAc.replyToMessage(msg, DataConnectionAc.RSP_REMOVE_APNCONTEXT);
+                    break;
+                }
+                case DataConnectionAc.REQ_GET_APNCONTEXT_LIST: {
+                    if (VDBG) log("REQ_GET_APNCONTEXT_LIST num in list=" + mApnList.size());
+                    mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_APNCONTEXT_LIST,
+                                       new ArrayList(mApnList));
+                    break;
+                }
+                case DataConnectionAc.REQ_SET_RECONNECT_INTENT: {
+                    PendingIntent intent = (PendingIntent) msg.obj;
+                    if (VDBG) log("REQ_SET_RECONNECT_INTENT");
+                    mReconnectIntent = intent;
+                    mAc.replyToMessage(msg, DataConnectionAc.RSP_SET_RECONNECT_INTENT);
+                    break;
+                }
+                case DataConnectionAc.REQ_GET_RECONNECT_INTENT: {
+                    if (VDBG) log("REQ_GET_RECONNECT_INTENT");
+                    mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_RECONNECT_INTENT,
+                                       mReconnectIntent);
+                    break;
+                }
                 case EVENT_CONNECT:
                     if (DBG) log("DcDefaultState: msg.what=EVENT_CONNECT, fail not expected");
                     ConnectionParams cp = (ConnectionParams) msg.obj;
@@ -553,14 +715,29 @@ public abstract class DataConnection extends HierarchicalStateMachine {
                     notifyDisconnectCompleted((DisconnectParams) msg.obj);
                     break;
 
+                case EVENT_RIL_CONNECTED:
+                    ar = (AsyncResult)msg.obj;
+                    if (ar.exception == null) {
+                        mRilVersion = (Integer)ar.result;
+                        if (DBG) {
+                            log("DcDefaultState: msg.what=EVENT_RIL_CONNECTED mRilVersion=" +
+                                mRilVersion);
+                        }
+                    } else {
+                        log("Unexpected exception on EVENT_RIL_CONNECTED");
+                        mRilVersion = -1;
+                    }
+                    break;
+
                 default:
                     if (DBG) {
-                        log("DcDefaultState: shouldn't happen but ignore msg.what=" + msg.what);
+                        log("DcDefaultState: shouldn't happen but ignore msg.what=0x" +
+                                Integer.toHexString(msg.what));
                     }
                     break;
             }
 
-            return true;
+            return HANDLED;
         }
     }
     private DcDefaultState mDefaultState = new DcDefaultState();
@@ -568,23 +745,26 @@ public abstract class DataConnection extends HierarchicalStateMachine {
     /**
      * The state machine is inactive and expects a EVENT_CONNECT.
      */
-    private class DcInactiveState extends HierarchicalState {
+    private class DcInactiveState extends State {
         private ConnectionParams mConnectionParams = null;
         private FailCause mFailCause = null;
         private DisconnectParams mDisconnectParams = null;
 
-        public void setEnterNotificationParams(ConnectionParams cp, FailCause cause) {
-            log("DcInactiveState: setEnterNoticationParams cp,cause");
+        public void setEnterNotificationParams(ConnectionParams cp, FailCause cause,
+                                               int retryOverride) {
+            if (VDBG) log("DcInactiveState: setEnterNoticationParams cp,cause");
             mConnectionParams = cp;
             mFailCause = cause;
+            mRetryOverride = retryOverride;
         }
 
         public void setEnterNotificationParams(DisconnectParams dp) {
-          log("DcInactiveState: setEnterNoticationParams dp");
+            if (VDBG) log("DcInactiveState: setEnterNoticationParams dp");
             mDisconnectParams = dp;
         }
 
-        @Override protected void enter() {
+        @Override
+        public void enter() {
             mTag += 1;
 
             /**
@@ -595,48 +775,56 @@ public abstract class DataConnection extends HierarchicalStateMachine {
              * call to isInactive.
              */
             if ((mConnectionParams != null) && (mFailCause != null)) {
-                log("DcInactiveState: enter notifyConnectCompleted");
+                if (VDBG) log("DcInactiveState: enter notifyConnectCompleted");
                 notifyConnectCompleted(mConnectionParams, mFailCause);
             }
             if (mDisconnectParams != null) {
-              log("DcInactiveState: enter notifyDisconnectCompleted");
+                if (VDBG) log("DcInactiveState: enter notifyDisconnectCompleted");
                 notifyDisconnectCompleted(mDisconnectParams);
             }
+            clearSettings();
         }
 
-        @Override protected void exit() {
+        @Override
+        public void exit() {
             // clear notifications
             mConnectionParams = null;
             mFailCause = null;
             mDisconnectParams = null;
         }
 
-        @Override protected boolean processMessage(Message msg) {
+        @Override
+        public boolean processMessage(Message msg) {
             boolean retVal;
 
             switch (msg.what) {
-                case EVENT_RESET:
+                case DataConnectionAc.REQ_RESET:
                     if (DBG) {
-                        log("DcInactiveState: msg.what=EVENT_RESET, ignore we're already reset");
+                        log("DcInactiveState: msg.what=RSP_RESET, ignore we're already reset");
                     }
-                    if (msg.obj != null) {
-                        notifyDisconnectCompleted((DisconnectParams) msg.obj);
-                    }
-                    retVal = true;
+                    mAc.replyToMessage(msg, DataConnectionAc.RSP_RESET);
+                    retVal = HANDLED;
                     break;
 
                 case EVENT_CONNECT:
-                    if (DBG) log("DcInactiveState msg.what=EVENT_CONNECT");
                     ConnectionParams cp = (ConnectionParams) msg.obj;
                     cp.tag = mTag;
+                    if (DBG) {
+                        log("DcInactiveState msg.what=EVENT_CONNECT." + "RefCount = "
+                                + mRefCount);
+                    }
+                    mRefCount = 1;
                     onConnect(cp);
                     transitionTo(mActivatingState);
-                    retVal = true;
+                    retVal = HANDLED;
                     break;
 
                 default:
-                    if (DBG) log("DcInactiveState nothandled msg.what=" + msg.what);
-                    retVal = false;
+                    if (VDBG) {
+                        log("DcInactiveState nothandled msg.what=0x" +
+                                Integer.toHexString(msg.what));
+                    }
+                    retVal = NOT_HANDLED;
                     break;
             }
             return retVal;
@@ -647,17 +835,26 @@ public abstract class DataConnection extends HierarchicalStateMachine {
     /**
      * The state machine is activating a connection.
      */
-    private class DcActivatingState extends HierarchicalState {
-        @Override protected boolean processMessage(Message msg) {
+    private class DcActivatingState extends State {
+        @Override
+        public boolean processMessage(Message msg) {
             boolean retVal;
             AsyncResult ar;
             ConnectionParams cp;
 
             switch (msg.what) {
                 case EVENT_DISCONNECT:
-                    if (DBG) log("DcActivatingState deferring msg.what=EVENT_DISCONNECT");
+                    if (DBG) log("DcActivatingState deferring msg.what=EVENT_DISCONNECT"
+                            + mRefCount);
+                    deferMessage(msg);
+                    retVal = HANDLED;
+                    break;
+
+                case EVENT_CONNECT:
+                    if (DBG) log("DcActivatingState deferring msg.what=EVENT_CONNECT refCount = "
+                            + mRefCount);
                     deferMessage(msg);
-                    retVal = true;
+                    retVal = HANDLED;
                     break;
 
                 case EVENT_SETUP_DATA_CONNECTION_DONE:
@@ -666,7 +863,7 @@ public abstract class DataConnection extends HierarchicalStateMachine {
                     ar = (AsyncResult) msg.obj;
                     cp = (ConnectionParams) ar.userObj;
 
-                    SetupResult result = onSetupConnectionCompleted(ar);
+                    DataCallState.SetupResult result = onSetupConnectionCompleted(ar);
                     if (DBG) log("DcActivatingState onSetupConnectionCompleted result=" + result);
                     switch (result) {
                         case SUCCESS:
@@ -678,7 +875,7 @@ public abstract class DataConnection extends HierarchicalStateMachine {
                             // Vendor ril rejected the command and didn't connect.
                             // Transition to inactive but send notifications after
                             // we've entered the mInactive state.
-                            mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
+                            mInactiveState.setEnterNotificationParams(cp, result.mFailCause, -1);
                             transitionTo(mInactiveState);
                             break;
                         case ERR_UnacceptableParameter:
@@ -693,16 +890,17 @@ public abstract class DataConnection extends HierarchicalStateMachine {
                             break;
                         case ERR_RilError:
                             // Request failed and mFailCause has the reason
-                            mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
+                            mInactiveState.setEnterNotificationParams(cp, result.mFailCause,
+                                                                      getSuggestedRetryTime(ar));
                             transitionTo(mInactiveState);
                             break;
                         case ERR_Stale:
                             // Request is stale, ignore.
                             break;
                         default:
-                            throw new RuntimeException("Unkown SetupResult, should not happen");
+                            throw new RuntimeException("Unknown SetupResult, should not happen");
                     }
-                    retVal = true;
+                    retVal = HANDLED;
                     break;
 
                 case EVENT_GET_LAST_FAIL_DONE:
@@ -718,8 +916,8 @@ public abstract class DataConnection extends HierarchicalStateMachine {
                         }
                         // Transition to inactive but send notifications after
                         // we've entered the mInactive state.
-                         mInactiveState.setEnterNotificationParams(cp, cause);
-                         transitionTo(mInactiveState);
+                        mInactiveState.setEnterNotificationParams(cp, cause, -1);
+                        transitionTo(mInactiveState);
                     } else {
                         if (DBG) {
                             log("DcActivatingState EVENT_GET_LAST_FAIL_DONE is stale cp.tag="
@@ -727,12 +925,15 @@ public abstract class DataConnection extends HierarchicalStateMachine {
                         }
                     }
 
-                    retVal = true;
+                    retVal = HANDLED;
                     break;
 
                 default:
-                    if (DBG) log("DcActivatingState not handled msg.what=" + msg.what);
-                    retVal = false;
+                    if (VDBG) {
+                        log("DcActivatingState not handled msg.what=0x" +
+                                Integer.toHexString(msg.what));
+                    }
+                    retVal = NOT_HANDLED;
                     break;
             }
             return retVal;
@@ -743,12 +944,12 @@ public abstract class DataConnection extends HierarchicalStateMachine {
     /**
      * The state machine is connected, expecting an EVENT_DISCONNECT.
      */
-    private class DcActiveState extends HierarchicalState {
+    private class DcActiveState extends State {
         private ConnectionParams mConnectionParams = null;
         private FailCause mFailCause = null;
 
         public void setEnterNotificationParams(ConnectionParams cp, FailCause cause) {
-            log("DcInactiveState: setEnterNoticationParams cp,cause");
+            if (VDBG) log("DcInactiveState: setEnterNoticationParams cp,cause");
             mConnectionParams = cp;
             mFailCause = cause;
         }
@@ -762,33 +963,54 @@ public abstract class DataConnection extends HierarchicalStateMachine {
              * call to isActive.
              */
             if ((mConnectionParams != null) && (mFailCause != null)) {
-                log("DcActiveState: enter notifyConnectCompleted");
+                if (VDBG) log("DcActiveState: enter notifyConnectCompleted");
                 notifyConnectCompleted(mConnectionParams, mFailCause);
             }
         }
 
-        @Override protected void exit() {
+        @Override
+        public void exit() {
             // clear notifications
             mConnectionParams = null;
             mFailCause = null;
         }
 
-        @Override protected boolean processMessage(Message msg) {
+        @Override
+        public boolean processMessage(Message msg) {
             boolean retVal;
 
             switch (msg.what) {
+                case EVENT_CONNECT:
+                    mRefCount++;
+                    if (DBG) log("DcActiveState msg.what=EVENT_CONNECT RefCount=" + mRefCount);
+                    if (msg.obj != null) {
+                        notifyConnectCompleted((ConnectionParams) msg.obj, FailCause.NONE);
+                    }
+                    retVal = HANDLED;
+                    break;
                 case EVENT_DISCONNECT:
-                    if (DBG) log("DcActiveState msg.what=EVENT_DISCONNECT");
-                    DisconnectParams dp = (DisconnectParams) msg.obj;
-                    dp.tag = mTag;
-                    tearDownData(dp);
-                    transitionTo(mDisconnectingState);
-                    retVal = true;
+                    mRefCount--;
+                    if (DBG) log("DcActiveState msg.what=EVENT_DISCONNECT RefCount=" + mRefCount);
+                    if (mRefCount == 0)
+                    {
+                        DisconnectParams dp = (DisconnectParams) msg.obj;
+                        dp.tag = mTag;
+                        tearDownData(dp);
+                        transitionTo(mDisconnectingState);
+                    } else {
+                        if (msg.obj != null) {
+                            notifyDisconnectCompleted((DisconnectParams) msg.obj);
+                        }
+                    }
+                    retVal = HANDLED;
                     break;
 
                 default:
-                    if (DBG) log("DcActiveState nothandled msg.what=" + msg.what);
-                    retVal = false;
+                    if (VDBG) {
+                        log("DcActiveState not handled msg.what=0x" +
+                                Integer.toHexString(msg.what));
+                    }
+                    retVal = NOT_HANDLED;
                     break;
             }
             return retVal;
@@ -799,11 +1021,19 @@ public abstract class DataConnection extends HierarchicalStateMachine {
     /**
      * The state machine is disconnecting.
      */
-    private class DcDisconnectingState extends HierarchicalState {
-        @Override protected boolean processMessage(Message msg) {
+    private class DcDisconnectingState extends State {
+        @Override
+        public boolean processMessage(Message msg) {
             boolean retVal;
 
             switch (msg.what) {
+                case EVENT_CONNECT:
+                    if (DBG) log("DcDisconnectingState msg.what=EVENT_CONNECT. Defer. RefCount = "
+                            + mRefCount);
+                    deferMessage(msg);
+                    retVal = HANDLED;
+                    break;
+
                 case EVENT_DEACTIVATE_DONE:
                     if (DBG) log("DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE");
                     AsyncResult ar = (AsyncResult) msg.obj;
@@ -817,12 +1047,15 @@ public abstract class DataConnection extends HierarchicalStateMachine {
                         if (DBG) log("DcDisconnectState EVENT_DEACTIVATE_DONE stale dp.tag="
                                 + dp.tag + " mTag=" + mTag);
                     }
-                    retVal = true;
+                    retVal = HANDLED;
                     break;
 
                 default:
-                    if (DBG) log("DcDisconnectingState not handled msg.what=" + msg.what);
-                    retVal = false;
+                    if (VDBG) {
+                        log("DcDisconnectingState not handled msg.what=0x" +
+                                Integer.toHexString(msg.what));
+                    }
+                    retVal = NOT_HANDLED;
                     break;
             }
             return retVal;
@@ -833,8 +1066,9 @@ public abstract class DataConnection extends HierarchicalStateMachine {
     /**
      * The state machine is disconnecting after an creating a connection.
      */
-    private class DcDisconnectionErrorCreatingConnection extends HierarchicalState {
-        @Override protected boolean processMessage(Message msg) {
+    private class DcDisconnectionErrorCreatingConnection extends State {
+        @Override
+        public boolean processMessage(Message msg) {
             boolean retVal;
 
             switch (msg.what) {
@@ -850,7 +1084,7 @@ public abstract class DataConnection extends HierarchicalStateMachine {
                         // Transition to inactive but send notifications after
                         // we've entered the mInactive state.
                         mInactiveState.setEnterNotificationParams(cp,
-                                FailCause.UNACCEPTABLE_NETWORK_PARAMETER);
+                                FailCause.UNACCEPTABLE_NETWORK_PARAMETER, -1);
                         transitionTo(mInactiveState);
                     } else {
                         if (DBG) {
@@ -858,15 +1092,15 @@ public abstract class DataConnection extends HierarchicalStateMachine {
                                     " stale dp.tag=" + cp.tag + ", mTag=" + mTag);
                         }
                     }
-                    retVal = true;
+                    retVal = HANDLED;
                     break;
 
                 default:
-                    if (DBG) {
-                        log("DcDisconnectionErrorCreatingConnection not handled msg.what="
-                                + msg.what);
+                    if (VDBG) {
+                        log("DcDisconnectionErrorCreatingConnection not handled msg.what=0x"
+                                + Integer.toHexString(msg.what));
                     }
-                    retVal = false;
+                    retVal = NOT_HANDLED;
                     break;
             }
             return retVal;
@@ -878,143 +1112,26 @@ public abstract class DataConnection extends HierarchicalStateMachine {
     // ******* public interface
 
     /**
-     * Disconnect from the network.
-     *
-     * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
-     *        With AsyncResult.userObj set to the original msg.obj.
-     */
-    public void reset(Message onCompletedMsg) {
-        sendMessage(obtainMessage(EVENT_RESET, new DisconnectParams(onCompletedMsg)));
-    }
-
-    /**
-     * Reset the connection and wait for it to complete.
-     * TODO: Remove when all callers only need the asynchronous
-     * reset defined above.
-     */
-    public void resetSynchronously() {
-        ResetSynchronouslyLock lockObj = new ResetSynchronouslyLock();
-        synchronized(lockObj) {
-            sendMessage(obtainMessage(EVENT_RESET, new DisconnectParams(lockObj)));
-            try {
-                lockObj.wait();
-            } catch (InterruptedException e) {
-                log("blockingReset: unexpected interrupted of wait()");
-            }
-        }
-    }
-
-    /**
-     * Connect to the apn and return an AsyncResult in onCompletedMsg.
+     * Bring up a connection to the apn and return an AsyncResult in onCompletedMsg.
      * Used for cellular networks that use Acesss Point Names (APN) such
      * as GSM networks.
      *
      * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
      *        With AsyncResult.userObj set to the original msg.obj,
      *        AsyncResult.result = FailCause and AsyncResult.exception = Exception().
-     * @param apn is the Access Point Name to connect to
+     * @param apn is the Access Point Name to bring up a connection to
      */
-    public void connect(Message onCompletedMsg, ApnSetting apn) {
+    public void bringUp(Message onCompletedMsg, ApnSetting apn) {
         sendMessage(obtainMessage(EVENT_CONNECT, new ConnectionParams(apn, onCompletedMsg)));
     }
 
     /**
-     * Connect to the apn and return an AsyncResult in onCompletedMsg.
-     *
-     * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
-     *        With AsyncResult.userObj set to the original msg.obj,
-     *        AsyncResult.result = FailCause and AsyncResult.exception = Exception().
-     */
-    public void connect(Message onCompletedMsg) {
-        sendMessage(obtainMessage(EVENT_CONNECT, new ConnectionParams(null, onCompletedMsg)));
-    }
-
-    /**
-     * Disconnect from the network.
+     * Tear down the connection through the apn on the network.
      *
      * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
      *        With AsyncResult.userObj set to the original msg.obj.
      */
-    public void disconnect(Message onCompletedMsg) {
-        sendMessage(obtainMessage(EVENT_DISCONNECT, new DisconnectParams(onCompletedMsg)));
-    }
-
-    // ****** The following are used for debugging.
-
-    /**
-     * TODO: This should be an asynchronous call and we wouldn't
-     * have to use handle the notification in the DcInactiveState.enter.
-     *
-     * @return true if the state machine is in the inactive state.
-     */
-    public boolean isInactive() {
-        boolean retVal = getCurrentState() == mInactiveState;
-        return retVal;
-    }
-
-    /**
-     * TODO: This should be an asynchronous call and we wouldn't
-     * have to use handle the notification in the DcActiveState.enter.
-     *
-     * @return true if the state machine is in the active state.
-     */
-    public boolean isActive() {
-        boolean retVal = getCurrentState() == mActiveState;
-        return retVal;
-    }
-
-    /**
-     * Get the DataConnection ID
-     */
-    public int getDataConnectionId() {
-        return mId;
-    }
-
-    /**
-     * Return the LinkProperties for the connection.
-     *
-     * @return a copy of the LinkProperties, is never null.
-     */
-    public LinkProperties getLinkProperties() {
-        return new LinkProperties(mLinkProperties);
-    }
-
-    /**
-     * A capability is an Integer/String pair, the capabilities
-     * are defined in the class LinkSocket#Key.
-     *
-     * @return a copy of this connections capabilities, may be empty but never null.
-     */
-    public LinkCapabilities getLinkCapabilities() {
-        return new LinkCapabilities(mCapabilities);
-    }
-
-    /**
-     * @return the current state as a string.
-     */
-    public String getStateAsString() {
-        String retVal = getCurrentState().getName();
-        return retVal;
-    }
-
-    /**
-     * @return the time of when this connection was created.
-     */
-    public long getConnectionTime() {
-        return createTime;
-    }
-
-    /**
-     * @return the time of the last failure.
-     */
-    public long getLastFailTime() {
-        return lastFailTime;
-    }
-
-    /**
-     * @return the last cause of failure.
-     */
-    public FailCause getLastFailCause() {
-        return lastFailCause;
+    public void tearDown(String reason, Message onCompletedMsg) {
+        sendMessage(obtainMessage(EVENT_DISCONNECT, new DisconnectParams(reason, onCompletedMsg)));
     }
 }
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionAc.java b/telephony/java/com/android/internal/telephony/DataConnectionAc.java
new file mode 100644 (file)
index 0000000..309dbed
--- /dev/null
@@ -0,0 +1,540 @@
+/*
+ * Copyright (C) 2011 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;
+
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+
+import android.app.PendingIntent;
+import android.net.LinkCapabilities;
+import android.net.LinkProperties;
+import android.net.ProxyProperties;
+import android.os.Message;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * AsyncChannel to a DataConnection
+ */
+public class DataConnectionAc extends AsyncChannel {
+    private static final boolean DBG = false;
+    private String mLogTag;
+
+    public DataConnection dataConnection;
+
+    public static final int BASE = Protocol.BASE_DATA_CONNECTION_AC;
+
+    public static final int REQ_IS_INACTIVE = BASE + 0;
+    public static final int RSP_IS_INACTIVE = BASE + 1;
+
+    public static final int REQ_GET_CID = BASE + 2;
+    public static final int RSP_GET_CID = BASE + 3;
+
+    public static final int REQ_GET_APNSETTING = BASE + 4;
+    public static final int RSP_GET_APNSETTING = BASE + 5;
+
+    public static final int REQ_GET_LINK_PROPERTIES = BASE + 6;
+    public static final int RSP_GET_LINK_PROPERTIES = BASE + 7;
+
+    public static final int REQ_SET_LINK_PROPERTIES_HTTP_PROXY = BASE + 8;
+    public static final int RSP_SET_LINK_PROPERTIES_HTTP_PROXY = BASE + 9;
+
+    public static final int REQ_GET_LINK_CAPABILITIES = BASE + 10;
+    public static final int RSP_GET_LINK_CAPABILITIES = BASE + 11;
+
+    public static final int REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE = BASE + 12;
+    public static final int RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE = BASE + 13;
+
+    public static final int REQ_RESET = BASE + 14;
+    public static final int RSP_RESET = BASE + 15;
+
+    public static final int REQ_GET_REFCOUNT = BASE + 16;
+    public static final int RSP_GET_REFCOUNT = BASE + 17;
+
+    public static final int REQ_ADD_APNCONTEXT = BASE + 18;
+    public static final int RSP_ADD_APNCONTEXT = BASE + 19;
+
+    public static final int REQ_REMOVE_APNCONTEXT = BASE + 20;
+    public static final int RSP_REMOVE_APNCONTEXT = BASE + 21;
+
+    public static final int REQ_GET_APNCONTEXT_LIST = BASE + 22;
+    public static final int RSP_GET_APNCONTEXT_LIST = BASE + 23;
+
+    public static final int REQ_SET_RECONNECT_INTENT = BASE + 24;
+    public static final int RSP_SET_RECONNECT_INTENT = BASE + 25;
+
+    public static final int REQ_GET_RECONNECT_INTENT = BASE + 26;
+    public static final int RSP_GET_RECONNECT_INTENT = BASE + 27;
+
+    /**
+     * enum used to notify action taken or necessary to be
+     * taken after the link property is changed.
+     */
+    public enum LinkPropertyChangeAction {
+        NONE, CHANGED, RESET;
+
+        public static LinkPropertyChangeAction fromInt(int value) {
+            if (value == NONE.ordinal()) {
+                return NONE;
+            } else if (value == CHANGED.ordinal()) {
+                return CHANGED;
+            } else if (value == RESET.ordinal()) {
+                return RESET;
+            } else {
+                throw new RuntimeException("LinkPropertyChangeAction.fromInt: bad value=" + value);
+            }
+        }
+    }
+
+    public DataConnectionAc(DataConnection dc, String logTag) {
+        dataConnection = dc;
+        mLogTag = logTag;
+    }
+
+    /**
+     * Request if the state machine is in the inactive state.
+     * Response {@link #rspIsInactive}
+     */
+    public void reqIsInactive() {
+        sendMessage(REQ_IS_INACTIVE);
+        if (DBG) log("reqIsInactive");
+    }
+
+    /**
+     * Evaluate RSP_IS_INACTIVE.
+     *
+     * @return true if the state machine is in the inactive state.
+     */
+    public boolean rspIsInactive(Message response) {
+        boolean retVal = response.arg1 == 1;
+        if (DBG) log("rspIsInactive=" + retVal);
+        return retVal;
+    }
+
+    /**
+     * @return true if the state machine is in the inactive state.
+     */
+    public boolean isInactiveSync() {
+        Message response = sendMessageSynchronously(REQ_IS_INACTIVE);
+        if ((response != null) && (response.what == RSP_IS_INACTIVE)) {
+            return rspIsInactive(response);
+        } else {
+            log("rspIsInactive error response=" + response);
+            return false;
+        }
+    }
+
+    /**
+     * Request the Connection ID.
+     * Response {@link #rspCid}
+     */
+    public void reqCid() {
+        sendMessage(REQ_GET_CID);
+        if (DBG) log("reqCid");
+    }
+
+    /**
+     * Evaluate a RSP_GET_CID message and return the cid.
+     *
+     * @param response Message
+     * @return connection id or -1 if an error
+     */
+    public int rspCid(Message response) {
+        int retVal = response.arg1;
+        if (DBG) log("rspCid=" + retVal);
+        return retVal;
+    }
+
+    /**
+     * @return connection id or -1 if an error
+     */
+    public int getCidSync() {
+        Message response = sendMessageSynchronously(REQ_GET_CID);
+        if ((response != null) && (response.what == RSP_GET_CID)) {
+            return rspCid(response);
+        } else {
+            log("rspCid error response=" + response);
+            return -1;
+        }
+    }
+
+    /**
+     * Request the Reference Count.
+     * Response {@link #rspRefCount}
+     */
+    public void reqRefCount() {
+        sendMessage(REQ_GET_REFCOUNT);
+        if (DBG) log("reqRefCount");
+    }
+
+    /**
+     * Evaluate a RSP_GET_REFCOUNT message and return the refCount.
+     *
+     * @param response Message
+     * @return ref count or -1 if an error
+     */
+    public int rspRefCount(Message response) {
+        int retVal = response.arg1;
+        if (DBG) log("rspRefCount=" + retVal);
+        return retVal;
+    }
+
+    /**
+     * @return connection id or -1 if an error
+     */
+    public int getRefCountSync() {
+        Message response = sendMessageSynchronously(REQ_GET_REFCOUNT);
+        if ((response != null) && (response.what == RSP_GET_REFCOUNT)) {
+            return rspRefCount(response);
+        } else {
+            log("rspRefCount error response=" + response);
+            return -1;
+        }
+    }
+
+    /**
+     * Request the connections ApnSetting.
+     * Response {@link #rspApnSetting}
+     */
+    public void reqApnSetting() {
+        sendMessage(REQ_GET_APNSETTING);
+        if (DBG) log("reqApnSetting");
+    }
+
+    /**
+     * Evaluate a RSP_APN_SETTING message and return the ApnSetting.
+     *
+     * @param response Message
+     * @return ApnSetting, maybe null
+     */
+    public ApnSetting rspApnSetting(Message response) {
+        ApnSetting retVal = (ApnSetting) response.obj;
+        if (DBG) log("rspApnSetting=" + retVal);
+        return retVal;
+    }
+
+    /**
+     * Get the connections ApnSetting.
+     *
+     * @return ApnSetting or null if an error
+     */
+    public ApnSetting getApnSettingSync() {
+        Message response = sendMessageSynchronously(REQ_GET_APNSETTING);
+        if ((response != null) && (response.what == RSP_GET_APNSETTING)) {
+            return rspApnSetting(response);
+        } else {
+            log("getApnSetting error response=" + response);
+            return null;
+        }
+    }
+
+    /**
+     * Request the connections LinkProperties.
+     * Response {@link #rspLinkProperties}
+     */
+    public void reqLinkProperties() {
+        sendMessage(REQ_GET_LINK_PROPERTIES);
+        if (DBG) log("reqLinkProperties");
+    }
+
+    /**
+     * Evaluate RSP_GET_LINK_PROPERTIES
+     *
+     * @param response
+     * @return LinkProperties, maybe null.
+     */
+    public LinkProperties rspLinkProperties(Message response) {
+        LinkProperties retVal = (LinkProperties) response.obj;
+        if (DBG) log("rspLinkProperties=" + retVal);
+        return retVal;
+    }
+
+    /**
+     * Get the connections LinkProperties.
+     *
+     * @return LinkProperties or null if an error
+     */
+    public LinkProperties getLinkPropertiesSync() {
+        Message response = sendMessageSynchronously(REQ_GET_LINK_PROPERTIES);
+        if ((response != null) && (response.what == RSP_GET_LINK_PROPERTIES)) {
+            return rspLinkProperties(response);
+        } else {
+            log("getLinkProperties error response=" + response);
+            return null;
+        }
+    }
+
+    /**
+     * Request setting the connections LinkProperties.HttpProxy.
+     * Response RSP_SET_LINK_PROPERTIES when complete.
+     */
+    public void reqSetLinkPropertiesHttpProxy(ProxyProperties proxy) {
+        sendMessage(REQ_SET_LINK_PROPERTIES_HTTP_PROXY, proxy);
+        if (DBG) log("reqSetLinkPropertiesHttpProxy proxy=" + proxy);
+    }
+
+    /**
+     * Set the connections LinkProperties.HttpProxy
+     */
+    public void setLinkPropertiesHttpProxySync(ProxyProperties proxy) {
+        Message response =
+            sendMessageSynchronously(REQ_SET_LINK_PROPERTIES_HTTP_PROXY, proxy);
+        if ((response != null) && (response.what == RSP_SET_LINK_PROPERTIES_HTTP_PROXY)) {
+            if (DBG) log("setLinkPropertiesHttpPoxy ok");
+        } else {
+            log("setLinkPropertiesHttpPoxy error response=" + response);
+        }
+    }
+
+    /**
+     * Request update LinkProperties from DataCallState
+     * Response {@link #rspUpdateLinkPropertiesDataCallState}
+     */
+    public void reqUpdateLinkPropertiesDataCallState(DataCallState newState) {
+        sendMessage(REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE, newState);
+        if (DBG) log("reqUpdateLinkPropertiesDataCallState");
+    }
+
+    public LinkPropertyChangeAction rspUpdateLinkPropertiesDataCallState(Message response) {
+        LinkPropertyChangeAction retVal = LinkPropertyChangeAction.fromInt(response.arg1);
+        if (DBG) log("rspUpdateLinkPropertiesState=" + retVal);
+        return retVal;
+    }
+
+    /**
+     * Update link properties in the data connection
+     *
+     * @return true if link property has been updated. false otherwise.
+     */
+    public LinkPropertyChangeAction updateLinkPropertiesDataCallStateSync(DataCallState newState) {
+        Message response =
+            sendMessageSynchronously(REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE, newState);
+        if ((response != null) &&
+            (response.what == RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE)) {
+            return rspUpdateLinkPropertiesDataCallState(response);
+        } else {
+            log("getLinkProperties error response=" + response);
+            return LinkPropertyChangeAction.NONE;
+        }
+    }
+
+    /**
+     * Request the connections LinkCapabilities.
+     * Response {@link #rspLinkCapabilities}
+     */
+    public void reqLinkCapabilities() {
+        sendMessage(REQ_GET_LINK_CAPABILITIES);
+        if (DBG) log("reqLinkCapabilities");
+    }
+
+    /**
+     * Evaluate RSP_GET_LINK_CAPABILITIES
+     *
+     * @param response
+     * @return LinkCapabilites, maybe null.
+     */
+    public LinkCapabilities rspLinkCapabilities(Message response) {
+        LinkCapabilities retVal = (LinkCapabilities) response.obj;
+        if (DBG) log("rspLinkCapabilities=" + retVal);
+        return retVal;
+    }
+
+    /**
+     * Get the connections LinkCapabilities.
+     *
+     * @return LinkCapabilities or null if an error
+     */
+    public LinkCapabilities getLinkCapabilitiesSync() {
+        Message response = sendMessageSynchronously(REQ_GET_LINK_CAPABILITIES);
+        if ((response != null) && (response.what == RSP_GET_LINK_CAPABILITIES)) {
+            return rspLinkCapabilities(response);
+        } else {
+            log("getLinkCapabilities error response=" + response);
+            return null;
+        }
+    }
+
+    /**
+     * Request the connections LinkCapabilities.
+     * Response RSP_RESET when complete
+     */
+    public void reqReset() {
+        sendMessage(REQ_RESET);
+        if (DBG) log("reqReset");
+    }
+
+    /**
+     * Reset the connection and wait for it to complete.
+     */
+    public void resetSync() {
+        Message response = sendMessageSynchronously(REQ_RESET);
+        if ((response != null) && (response.what == RSP_RESET)) {
+            if (DBG) log("restSync ok");
+        } else {
+            log("restSync error response=" + response);
+        }
+    }
+
+    /**
+     * Request to add ApnContext association.
+     * Response RSP_ADD_APNCONTEXT when complete.
+     */
+    public void reqAddApnContext(ApnContext apnContext) {
+        Message response = sendMessageSynchronously(REQ_ADD_APNCONTEXT, apnContext);
+        if (DBG) log("reqAddApnContext");
+    }
+
+    /**
+     * Add ApnContext association synchronoulsy.
+     *
+     * @param ApnContext to associate
+     */
+    public void addApnContextSync(ApnContext apnContext) {
+        Message response = sendMessageSynchronously(REQ_ADD_APNCONTEXT, apnContext);
+        if ((response != null) && (response.what == RSP_ADD_APNCONTEXT)) {
+            if (DBG) log("addApnContext ok");
+        } else {
+            log("addApnContext error response=" + response);
+        }
+    }
+
+    /**
+     * Request to remove ApnContext association.
+     * Response RSP_REMOVE_APNCONTEXT when complete.
+     */
+    public void reqRemomveApnContext(ApnContext apnContext) {
+        Message response = sendMessageSynchronously(REQ_REMOVE_APNCONTEXT, apnContext);
+        if (DBG) log("reqRemomveApnContext");
+    }
+
+    /**
+     * Remove ApnContext associateion.
+     *
+     * @param ApnContext to dissociate
+     */
+    public void removeApnContextSync(ApnContext apnContext) {
+        Message response = sendMessageSynchronously(REQ_REMOVE_APNCONTEXT, apnContext);
+        if ((response != null) && (response.what == RSP_REMOVE_APNCONTEXT)) {
+            if (DBG) log("removeApnContext ok");
+        } else {
+            log("removeApnContext error response=" + response);
+        }
+    }
+
+    /**
+     * Request to retrive ApnContext List associated with DC.
+     * Response RSP_GET_APNCONTEXT_LIST when complete.
+     */
+    public void reqGetApnList(ApnContext apnContext) {
+        Message response = sendMessageSynchronously(REQ_GET_APNCONTEXT_LIST);
+        if (DBG) log("reqGetApnList");
+    }
+
+    /**
+     * Retrieve Collection of ApnContext from the response message.
+     *
+     * @param Message sent from DC in response to REQ_GET_APNCONTEXT_LIST.
+     * @return Collection of ApnContext
+     */
+    public Collection<ApnContext> rspApnList(Message response) {
+        Collection<ApnContext> retVal = (Collection<ApnContext>)response.obj;
+        if (retVal == null) retVal = new ArrayList<ApnContext>();
+        return retVal;
+    }
+
+    /**
+     * Retrieve collection of ApnContext currently associated with
+     * the DataConnectionA synchronously.
+     *
+     * @return Collection of ApnContext
+     */
+    public Collection<ApnContext> getApnListSync() {
+        Message response = sendMessageSynchronously(REQ_GET_APNCONTEXT_LIST);
+        if ((response != null) && (response.what == RSP_GET_APNCONTEXT_LIST)) {
+            if (DBG) log("getApnList ok");
+            return rspApnList(response);
+        } else {
+            log("getApnList error response=" + response);
+            // return dummy list with no entry
+            return new ArrayList<ApnContext>();
+        }
+    }
+
+    /**
+     * Request to set Pending ReconnectIntent to DC.
+     * Response RSP_SET_RECONNECT_INTENT when complete.
+     */
+    public void reqSetReconnectIntent(PendingIntent intent) {
+        Message response = sendMessageSynchronously(REQ_SET_RECONNECT_INTENT, intent);
+        if (DBG) log("reqSetReconnectIntent");
+    }
+
+    /**
+     * Set pending reconnect intent to DC synchronously.
+     *
+     * @param PendingIntent to set.
+     */
+    public void setReconnectIntentSync(PendingIntent intent) {
+        Message response = sendMessageSynchronously(REQ_SET_RECONNECT_INTENT, intent);
+        if ((response != null) && (response.what == RSP_SET_RECONNECT_INTENT)) {
+            if (DBG) log("setReconnectIntent ok");
+        } else {
+            log("setReconnectIntent error response=" + response);
+        }
+    }
+
+    /**
+     * Request to get Pending ReconnectIntent to DC.
+     * Response RSP_GET_RECONNECT_INTENT when complete.
+     */
+    public void reqGetReconnectIntent() {
+        Message response = sendMessageSynchronously(REQ_GET_RECONNECT_INTENT);
+        if (DBG) log("reqGetReconnectIntent");
+    }
+
+    /**
+     * Retrieve reconnect intent from response message from DC.
+     *
+     * @param Message which contains the reconnect intent.
+     * @return PendingIntent from the response.
+     */
+    public PendingIntent rspReconnectIntent(Message response) {
+        PendingIntent retVal = (PendingIntent) response.obj;
+        return retVal;
+    }
+
+    /**
+     * Retrieve reconnect intent currently set in DC synchronously.
+     *
+     * @return PendingIntent reconnect intent current ly set in DC
+     */
+    public PendingIntent getReconnectIntentSync() {
+        Message response = sendMessageSynchronously(REQ_GET_RECONNECT_INTENT);
+        if ((response != null) && (response.what == RSP_GET_RECONNECT_INTENT)) {
+            if (DBG) log("getReconnectIntent ok");
+            return rspReconnectIntent(response);
+        } else {
+            log("getReconnectIntent error response=" + response);
+            return null;
+        }
+    }
+
+    private void log(String s) {
+        android.util.Log.d(mLogTag, "DataConnectionAc " + s);
+    }
+}
index 7f8485b..977b412 100644 (file)
@@ -22,24 +22,30 @@ import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
-import android.net.IConnectivityManager;
 import android.net.LinkCapabilities;
 import android.net.LinkProperties;
 import android.net.NetworkInfo;
 import android.net.wifi.WifiManager;
 import android.os.AsyncResult;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Messenger;
-import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.internal.R;
+import com.android.internal.telephony.DataConnection.FailCause;
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -87,38 +93,39 @@ public abstract class DataConnectionTracker extends Handler {
     public static String EXTRA_MESSENGER = "EXTRA_MESSENGER";
 
     /***** Event Codes *****/
-    protected static final int EVENT_DATA_SETUP_COMPLETE = 1;
-    protected static final int EVENT_RADIO_AVAILABLE = 3;
-    protected static final int EVENT_RECORDS_LOADED = 4;
-    protected static final int EVENT_TRY_SETUP_DATA = 5;
-    protected static final int EVENT_DATA_STATE_CHANGED = 6;
-    protected static final int EVENT_POLL_PDP = 7;
-    protected static final int EVENT_GET_PDP_LIST_COMPLETE = 11;
-    protected static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 12;
-    protected static final int EVENT_VOICE_CALL_STARTED = 14;
-    protected static final int EVENT_VOICE_CALL_ENDED = 15;
-    protected static final int EVENT_GPRS_DETACHED = 19;
-    protected static final int EVENT_LINK_STATE_CHANGED = 20;
-    protected static final int EVENT_ROAMING_ON = 21;
-    protected static final int EVENT_ROAMING_OFF = 22;
-    protected static final int EVENT_ENABLE_NEW_APN = 23;
-    protected static final int EVENT_RESTORE_DEFAULT_APN = 24;
-    protected static final int EVENT_DISCONNECT_DONE = 25;
-    protected static final int EVENT_GPRS_ATTACHED = 26;
-    protected static final int EVENT_START_NETSTAT_POLL = 27;
-    protected static final int EVENT_START_RECOVERY = 28;
-    protected static final int EVENT_APN_CHANGED = 29;
-    protected static final int EVENT_CDMA_DATA_DETACHED = 30;
-    protected static final int EVENT_NV_READY = 31;
-    protected static final int EVENT_PS_RESTRICT_ENABLED = 32;
-    protected static final int EVENT_PS_RESTRICT_DISABLED = 33;
-    public static final int EVENT_CLEAN_UP_CONNECTION = 34;
-    protected static final int EVENT_CDMA_OTA_PROVISION = 35;
-    protected static final int EVENT_RESTART_RADIO = 36;
-    protected static final int EVENT_SET_INTERNAL_DATA_ENABLE = 37;
-    protected static final int EVENT_RESET_DONE = 38;
-
-    public static final int CMD_SET_DATA_ENABLE = 39;
+    protected static final int BASE = Protocol.BASE_DATA_CONNECTION_TRACKER;
+    protected static final int EVENT_DATA_SETUP_COMPLETE = BASE + 0;
+    protected static final int EVENT_RADIO_AVAILABLE = BASE + 1;
+    protected static final int EVENT_RECORDS_LOADED = BASE + 2;
+    protected static final int EVENT_TRY_SETUP_DATA = BASE + 3;
+    protected static final int EVENT_DATA_STATE_CHANGED = BASE + 4;
+    protected static final int EVENT_POLL_PDP = BASE + 5;
+    protected static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = BASE + 6;
+    protected static final int EVENT_VOICE_CALL_STARTED = BASE + 7;
+    protected static final int EVENT_VOICE_CALL_ENDED = BASE + 8;
+    protected static final int EVENT_DATA_CONNECTION_DETACHED = BASE + 9;
+    protected static final int EVENT_LINK_STATE_CHANGED = BASE + 10;
+    protected static final int EVENT_ROAMING_ON = BASE + 11;
+    protected static final int EVENT_ROAMING_OFF = BASE + 12;
+    protected static final int EVENT_ENABLE_NEW_APN = BASE + 13;
+    protected static final int EVENT_RESTORE_DEFAULT_APN = BASE + 14;
+    protected static final int EVENT_DISCONNECT_DONE = BASE + 15;
+    protected static final int EVENT_DATA_CONNECTION_ATTACHED = BASE + 16;
+    protected static final int EVENT_START_NETSTAT_POLL = BASE + 17;
+    protected static final int EVENT_START_RECOVERY = BASE + 18;
+    protected static final int EVENT_APN_CHANGED = BASE + 19;
+    protected static final int EVENT_CDMA_DATA_DETACHED = BASE + 20;
+    protected static final int EVENT_NV_READY = BASE + 21;
+    protected static final int EVENT_PS_RESTRICT_ENABLED = BASE + 22;
+    protected static final int EVENT_PS_RESTRICT_DISABLED = BASE + 23;
+    public static final int EVENT_CLEAN_UP_CONNECTION = BASE + 24;
+    protected static final int EVENT_CDMA_OTA_PROVISION = BASE + 25;
+    protected static final int EVENT_RESTART_RADIO = BASE + 26;
+    protected static final int EVENT_SET_INTERNAL_DATA_ENABLE = BASE + 27;
+    protected static final int EVENT_RESET_DONE = BASE + 28;
+    public static final int CMD_SET_DATA_ENABLE = BASE + 29;
+    public static final int EVENT_CLEAN_UP_ALL_CONNECTIONS = BASE + 30;
+    public static final int CMD_SET_DEPENDENCY_MET = BASE + 31;
 
     /***** Constants *****/
 
@@ -128,11 +135,21 @@ public abstract class DataConnectionTracker extends Handler {
     protected static final int APN_SUPL_ID = 2;
     protected static final int APN_DUN_ID = 3;
     protected static final int APN_HIPRI_ID = 4;
-    protected static final int APN_NUM_TYPES = 5;
+    protected static final int APN_IMS_ID = 5;
+    protected static final int APN_FOTA_ID = 6;
+    protected static final int APN_CBS_ID = 7;
+    protected static final int APN_NUM_TYPES = 8;
 
     public static final int DISABLED = 0;
     public static final int ENABLED = 1;
 
+    public static final String APN_TYPE_KEY = "apnType";
+
+    /** Delay between APN attempts.
+        Note the property override mechanism is there just for testing purpose only. */
+    protected static final int APN_DELAY_MILLIS =
+                                SystemProperties.getInt("persist.radio.apn_delay", 5000);
+
     // responds to the setInternalDataEnabled call - used internally to turn off data
     // for example during emergency calls
     protected boolean mInternalDataEnabled = true;
@@ -192,6 +209,21 @@ public abstract class DataConnectionTracker extends Handler {
     //       getActionIntentReconnectAlarm.
     protected static final String INTENT_RECONNECT_ALARM_EXTRA_REASON = "reason";
 
+    // Used for debugging. Send the INTENT with an optional counter value with the number
+    // of times the setup is to fail before succeeding. If the counter isn't passed the
+    // setup will fail once. Example fail two times with FailCause.SIGNAL_LOST(-3)
+    // adb shell am broadcast \
+    //  -a com.android.internal.telephony.dataconnectiontracker.intent_set_fail_data_setup_counter \
+    //  --ei fail_data_setup_counter 3 --ei fail_data_setup_fail_cause -3
+    protected static final String INTENT_SET_FAIL_DATA_SETUP_COUNTER =
+        "com.android.internal.telephony.dataconnectiontracker.intent_set_fail_data_setup_counter";
+    protected static final String FAIL_DATA_SETUP_COUNTER = "fail_data_setup_counter";
+    protected int mFailDataSetupCounter = 0;
+    protected static final String FAIL_DATA_SETUP_FAIL_CAUSE = "fail_data_setup_fail_cause";
+    protected FailCause mFailDataSetupFailCause = FailCause.ERROR_UNSPECIFIED;
+
+    protected static final String DEFALUT_DATA_ON_BOOT_PROP = "net.def_data_on_boot";
+
     // member variables
     protected PhoneBase mPhone;
     protected Activity mActivity = Activity.NONE;
@@ -206,9 +238,6 @@ public abstract class DataConnectionTracker extends Handler {
     protected int mNoRecvPollCount = 0;
     protected boolean mNetStatPollEnabled = false;
 
-    /** Manage the behavior of data retry after failure (TODO: One per connection in the future?) */
-    protected RetryManager mRetryMgr = new RetryManager();
-
     // wifi connection status will be updated by sticky intent
     protected boolean mIsWifiConnected = false;
 
@@ -221,7 +250,7 @@ public abstract class DataConnectionTracker extends Handler {
     /** indication of our availability (preconditions to trysetupData are met) **/
     protected boolean mAvailability = false;
 
-    // When false we will not auto attach and manully attaching is required.
+    // When false we will not auto attach and manually attaching is required.
     protected boolean mAutoAttachOnCreation = false;
 
     // State of screen
@@ -229,12 +258,6 @@ public abstract class DataConnectionTracker extends Handler {
     //        really a lower power mode")
     protected boolean mIsScreenOn = true;
 
-    /** The link properties (dns, gateway, ip, etc) */
-    protected LinkProperties mLinkProperties = new LinkProperties();
-
-    /** The link capabilities */
-    protected LinkCapabilities mLinkCapabilities = new LinkCapabilities();
-
     /** Allows the generation of unique Id's for DataConnection objects */
     protected AtomicInteger mUniqueIdGenerator = new AtomicInteger(0);
 
@@ -242,15 +265,39 @@ public abstract class DataConnectionTracker extends Handler {
     protected HashMap<Integer, DataConnection> mDataConnections =
         new HashMap<Integer, DataConnection>();
 
+    /** The data connection async channels */
+    protected HashMap<Integer, DataConnectionAc> mDataConnectionAsyncChannels =
+        new HashMap<Integer, DataConnectionAc>();
+
+    /** Convert an ApnType string to Id (TODO: Use "enumeration" instead of String for ApnType) */
+    protected HashMap<String, Integer> mApnToDataConnectionId =
+                                    new HashMap<String, Integer>();
+
+    /** Phone.APN_TYPE_* ===> ApnContext */
+    protected ConcurrentHashMap<String, ApnContext> mApnContexts;
+
     /* Currently active APN */
     protected ApnSetting mActiveApn;
 
+    /** allApns holds all apns */
+    protected ArrayList<ApnSetting> mAllApns = null;
+
+    /** preferred apn */
+    protected ApnSetting mPreferredApn = null;
+
+    /** Is packet service restricted by network */
+    protected boolean mIsPsRestricted = false;
+
+    /* Once disposed dont handle any messages */
+    protected boolean mIsDisposed = false;
+
     protected BroadcastReceiver mIntentReceiver = new BroadcastReceiver ()
     {
         @Override
         public void onReceive(Context context, Intent intent)
         {
             String action = intent.getAction();
+            if (DBG) log("onReceive: action=" + action);
             if (action.equals(Intent.ACTION_SCREEN_ON)) {
                 mIsScreenOn = true;
                 stopNetStatPoll();
@@ -259,17 +306,10 @@ public abstract class DataConnectionTracker extends Handler {
                 mIsScreenOn = false;
                 stopNetStatPoll();
                 startNetStatPoll();
-            } else if (action.equals(getActionIntentReconnectAlarm())) {
+            } else if (action.startsWith(getActionIntentReconnectAlarm())) {
                 log("Reconnect alarm. Previous state was " + mState);
+                onActionIntentReconnectAlarm(intent);
 
-                String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON);
-                if (mState == State.FAILED) {
-                    Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
-                    msg.arg1 = 0; // tearDown is false
-                    msg.obj = reason;
-                    sendMessage(msg);
-                }
-                sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA));
             } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
                 final android.net.NetworkInfo networkInfo = (NetworkInfo)
                         intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
@@ -283,10 +323,48 @@ public abstract class DataConnectionTracker extends Handler {
                     // quit and won't report disconnected until next enabling.
                     mIsWifiConnected = false;
                 }
+            } else if (action.equals(INTENT_SET_FAIL_DATA_SETUP_COUNTER)) {
+                mFailDataSetupCounter = intent.getIntExtra(FAIL_DATA_SETUP_COUNTER, 1);
+                mFailDataSetupFailCause = FailCause.fromInt(
+                        intent.getIntExtra(FAIL_DATA_SETUP_FAIL_CAUSE,
+                                                    FailCause.ERROR_UNSPECIFIED.getErrorCode()));
+                if (DBG) log("set mFailDataSetupCounter=" + mFailDataSetupCounter +
+                        " mFailDataSetupFailCause=" + mFailDataSetupFailCause);
             }
         }
     };
 
+    protected boolean isDataSetupCompleteOk(AsyncResult ar) {
+        if (ar.exception != null) {
+            if (DBG) log("isDataSetupCompleteOk return false, ar.result=" + ar.result);
+            return false;
+        }
+        if (mFailDataSetupCounter <= 0) {
+            if (DBG) log("isDataSetupCompleteOk return true");
+            return true;
+        }
+        ar.result = mFailDataSetupFailCause;
+        if (DBG) {
+            log("isDataSetupCompleteOk return false" +
+                    " mFailDataSetupCounter=" + mFailDataSetupCounter +
+                    " mFailDataSetupFailCause=" + mFailDataSetupFailCause);
+        }
+        mFailDataSetupCounter -= 1;
+        return false;
+    }
+
+    protected void onActionIntentReconnectAlarm(Intent intent) {
+        String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON);
+        if (mState == State.FAILED) {
+            Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
+            msg.arg1 = 0; // tearDown is false
+            msg.arg2 = 0;
+            msg.obj = reason;
+            sendMessage(msg);
+        }
+        sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA));
+    }
+
     /**
      * Default constructor
      */
@@ -300,6 +378,7 @@ public abstract class DataConnectionTracker extends Handler {
         filter.addAction(Intent.ACTION_SCREEN_OFF);
         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+        filter.addAction(INTENT_SET_FAIL_DATA_SETUP_COUNTER);
 
         mDataEnabled = Settings.Secure.getInt(mPhone.getContext().getContentResolver(),
                 Settings.Secure.MOBILE_DATA, 1) == 1;
@@ -310,16 +389,23 @@ public abstract class DataConnectionTracker extends Handler {
 
         // This preference tells us 1) initial condition for "dataEnabled",
         // and 2) whether the RIL will setup the baseband to auto-PS attach.
-        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
-        dataEnabled[APN_DEFAULT_ID] =
-                !sp.getBoolean(PhoneBase.DATA_DISABLED_ON_BOOT_KEY, false);
+
+        dataEnabled[APN_DEFAULT_ID] = SystemProperties.getBoolean(DEFALUT_DATA_ON_BOOT_PROP,
+                                                                  true);
         if (dataEnabled[APN_DEFAULT_ID]) {
             enabledCount++;
         }
-        mAutoAttachOnCreation = dataEnabled[APN_DEFAULT_ID];
+
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
+        mAutoAttachOnCreation = sp.getBoolean(PhoneBase.DATA_DISABLED_ON_BOOT_KEY, false);
     }
 
     public void dispose() {
+        for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
+            dcac.disconnect();
+        }
+        mDataConnectionAsyncChannels.clear();
+        mIsDisposed = true;
         mPhone.getContext().unregisterReceiver(this.mIntentReceiver);
     }
 
@@ -333,34 +419,26 @@ public abstract class DataConnectionTracker extends Handler {
         return mActivity;
     }
 
-    public State getState() {
-        return mState;
-    }
-
-    public String getStateInString() {
-        switch (mState) {
-            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";
+    public boolean isApnTypeActive(String type) {
+        // TODO: support simultaneous with List instead
+        if (Phone.APN_TYPE_DUN.equals(type)) {
+            ApnSetting dunApn = fetchDunApn();
+            if (dunApn != null) {
+                return ((mActiveApn != null) && (dunApn.toString().equals(mActiveApn.toString())));
+            }
         }
+        return mActiveApn != null && mActiveApn.canHandleType(type);
     }
 
-    /**
-     * @return the data connections
-     */
-    public ArrayList<DataConnection> getAllDataConnections() {
-        /** TODO: change return type to Collection? */
-        return new ArrayList<DataConnection>(mDataConnections.values());
-    }
+    protected ApnSetting fetchDunApn() {
+        Context c = mPhone.getContext();
+        String apnData = Settings.Secure.getString(c.getContentResolver(),
+                Settings.Secure.TETHER_DUN_APN);
+        ApnSetting dunSetting = ApnSetting.fromString(apnData);
+        if (dunSetting != null) return dunSetting;
 
-    public boolean isApnTypeActive(String type) {
-        // TODO: support simultaneous with List instead
-        return mActiveApn != null && mActiveApn.canHandleType(type);
+        apnData = c.getResources().getString(R.string.config_tether_apndata);
+        return ApnSetting.fromString(apnData);
     }
 
     public String[] getActiveApnTypes() {
@@ -374,17 +452,8 @@ public abstract class DataConnectionTracker extends Handler {
         return result;
     }
 
-    public String getActiveApnType() {
-        String result;
-        if (mActiveApn != null) {
-            result = apnIdToType(mActiveApn.id);
-        } else {
-            result = null;
-        }
-        return result;
-    }
-
-    protected String getActiveApnString() {
+    /** TODO: See if we can remove */
+    public String getActiveApnString(String apnType) {
         String result = null;
         if (mActiveApn != null) {
             result = mActiveApn.apn;
@@ -392,17 +461,6 @@ public abstract class DataConnectionTracker extends Handler {
         return result;
     }
 
-    /**
-     * The data connection is expected to be setup while device
-     *  1. has Icc card
-     *  2. registered for data 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.
-     */
-    public abstract boolean isDataConnectionAsDesired();
-
     //The data roaming setting is now located in the shared preferences.
     //  See if the requested preference value is the same as that stored in
     //  the shared values.  If it is not, then update it.
@@ -412,7 +470,7 @@ public abstract class DataConnectionTracker extends Handler {
                 Settings.Secure.DATA_ROAMING, enabled ? 1 : 0);
             if (mPhone.getServiceState().getRoaming()) {
                 if (enabled) {
-                    mRetryMgr.resetRetryCount();
+                    resetAllRetryCounts();
                 }
                 sendMessage(obtainMessage(EVENT_ROAMING_ON));
             }
@@ -429,10 +487,19 @@ public abstract class DataConnectionTracker extends Handler {
         }
     }
 
-
+    // abstract methods
     protected abstract String getActionIntentReconnectAlarm();
+    protected abstract void startNetStatPoll();
+    protected abstract void stopNetStatPoll();
+    protected abstract void restartRadio();
+    protected abstract void log(String s);
+    protected abstract void loge(String s);
+    protected abstract boolean isDataAllowed();
+    protected abstract boolean isApnTypeAvailable(String type);
+    public    abstract State getState(String apnType);
+    protected abstract void setState(State s);
+    protected abstract void gotoIdleAndNotifyDataConnection(String reason);
 
-    // abstract handler methods
     protected abstract boolean onTrySetupData(String reason);
     protected abstract void onRoamingOff();
     protected abstract void onRoamingOn();
@@ -440,15 +507,22 @@ public abstract class DataConnectionTracker extends Handler {
     protected abstract void onRadioOffOrNotAvailable();
     protected abstract void onDataSetupComplete(AsyncResult ar);
     protected abstract void onDisconnectDone(int connId, AsyncResult ar);
-    protected abstract void onResetDone(AsyncResult ar);
     protected abstract void onVoiceCallStarted();
     protected abstract void onVoiceCallEnded();
-    protected abstract void onCleanUpConnection(boolean tearDown, String reason);
+    protected abstract void onCleanUpConnection(boolean tearDown, int apnId, String reason);
+    protected abstract void onCleanUpAllConnections(String cause);
+    protected abstract boolean isDataPossible(String apnType);
 
     @Override
     public void handleMessage(Message msg) {
         switch (msg.what) {
-
+            case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+                log("DISCONNECTED_CONNECTED: msg=" + msg);
+                DataConnectionAc dcac = (DataConnectionAc) msg.obj;
+                mDataConnectionAsyncChannels.remove(dcac.dataConnection.getDataConnectionId());
+                dcac.disconnected();
+                break;
+            }
             case EVENT_ENABLE_NEW_APN:
                 onEnableApn(msg.arg1, msg.arg2);
                 break;
@@ -463,7 +537,7 @@ public abstract class DataConnectionTracker extends Handler {
 
             case EVENT_ROAMING_OFF:
                 if (getDataOnRoamingEnabled() == false) {
-                    mRetryMgr.resetRetryCount();
+                    resetAllRetryCounts();
                 }
                 onRoamingOff();
                 break;
@@ -498,28 +572,47 @@ public abstract class DataConnectionTracker extends Handler {
                 onVoiceCallEnded();
                 break;
 
-            case EVENT_CLEAN_UP_CONNECTION:
+            case EVENT_CLEAN_UP_ALL_CONNECTIONS: {
+                onCleanUpAllConnections((String) msg.obj);
+                break;
+            }
+            case EVENT_CLEAN_UP_CONNECTION: {
                 boolean tearDown = (msg.arg1 == 0) ? false : true;
-                onCleanUpConnection(tearDown, (String) msg.obj);
+                onCleanUpConnection(tearDown, msg.arg2, (String) msg.obj);
                 break;
-
+            }
             case EVENT_SET_INTERNAL_DATA_ENABLE: {
                 boolean enabled = (msg.arg1 == ENABLED) ? true : false;
                 onSetInternalDataEnabled(enabled);
                 break;
             }
-            case EVENT_RESET_DONE:
+            case EVENT_RESET_DONE: {
+                if (DBG) log("EVENT_RESET_DONE");
                 onResetDone((AsyncResult) msg.obj);
                 break;
+            }
             case CMD_SET_DATA_ENABLE: {
-                log("CMD_SET_DATA_ENABLE msg=" + msg);
                 boolean enabled = (msg.arg1 == ENABLED) ? true : false;
+                if (DBG) log("CMD_SET_DATA_ENABLE enabled=" + enabled);
                 onSetDataEnabled(enabled);
                 break;
             }
 
+            case CMD_SET_DEPENDENCY_MET: {
+                boolean met = (msg.arg1 == ENABLED) ? true : false;
+                if (DBG) log("CMD_SET_DEPENDENCY_MET met=" + met);
+                Bundle bundle = msg.getData();
+                if (bundle != null) {
+                    String apnType = (String)bundle.get(APN_TYPE_KEY);
+                    if (apnType != null) {
+                        onSetDependencyMet(apnType, met);
+                    }
+                }
+                break;
+            }
+
             default:
-                Log.e("DATA", "Unidentified event = " + msg.what);
+                Log.e("DATA", "Unidentified event msg=" + msg);
                 break;
         }
     }
@@ -536,16 +629,6 @@ public abstract class DataConnectionTracker extends Handler {
         return result;
     }
 
-    protected abstract void startNetStatPoll();
-
-    protected abstract void stopNetStatPoll();
-
-    protected abstract void restartRadio();
-
-    protected abstract void log(String s);
-
-    protected abstract void loge(String s);
-
     protected int apnTypeToId(String type) {
         if (TextUtils.equals(type, Phone.APN_TYPE_DEFAULT)) {
             return APN_DEFAULT_ID;
@@ -557,6 +640,12 @@ public abstract class DataConnectionTracker extends Handler {
             return APN_DUN_ID;
         } else if (TextUtils.equals(type, Phone.APN_TYPE_HIPRI)) {
             return APN_HIPRI_ID;
+        } else if (TextUtils.equals(type, Phone.APN_TYPE_IMS)) {
+            return APN_IMS_ID;
+        } else if (TextUtils.equals(type, Phone.APN_TYPE_FOTA)) {
+            return APN_FOTA_ID;
+        } else if (TextUtils.equals(type, Phone.APN_TYPE_CBS)) {
+            return APN_CBS_ID;
         } else {
             return APN_INVALID_ID;
         }
@@ -574,20 +663,25 @@ public abstract class DataConnectionTracker extends Handler {
             return Phone.APN_TYPE_DUN;
         case APN_HIPRI_ID:
             return Phone.APN_TYPE_HIPRI;
+        case APN_IMS_ID:
+            return Phone.APN_TYPE_IMS;
+        case APN_FOTA_ID:
+            return Phone.APN_TYPE_FOTA;
+        case APN_CBS_ID:
+            return Phone.APN_TYPE_CBS;
         default:
             log("Unknown id (" + id + ") in apnIdToType");
             return Phone.APN_TYPE_DEFAULT;
         }
     }
 
-    protected abstract boolean isApnTypeAvailable(String type);
-
-    protected abstract void setState(State s);
-
     protected LinkProperties getLinkProperties(String apnType) {
         int id = apnTypeToId(apnType);
+
         if (isApnIdEnabled(id)) {
-            return new LinkProperties(mLinkProperties);
+            // TODO - remove this cdma-only hack and support multiple DCs.
+            DataConnectionAc dcac = mDataConnectionAsyncChannels.get(0);
+            return dcac.getLinkPropertiesSync();
         } else {
             return new LinkProperties();
         }
@@ -596,33 +690,14 @@ public abstract class DataConnectionTracker extends Handler {
     protected LinkCapabilities getLinkCapabilities(String apnType) {
         int id = apnTypeToId(apnType);
         if (isApnIdEnabled(id)) {
-            return new LinkCapabilities(mLinkCapabilities);
+            // TODO - remove this cdma-only hack and support multiple DCs.
+            DataConnectionAc dcac = mDataConnectionAsyncChannels.get(0);
+            return dcac.getLinkCapabilitiesSync();
         } else {
             return new LinkCapabilities();
         }
     }
 
-    /**
-     * Return the LinkProperties for the connection.
-     *
-     * @param connection
-     * @return a copy of the LinkProperties, is never null.
-     */
-    protected LinkProperties getLinkProperties(DataConnection connection) {
-        return connection.getLinkProperties();
-    }
-
-    /**
-     * A capability is an Integer/String pair, the capabilities
-     * are defined in the class LinkSocket#Key.
-     *
-     * @param connection
-     * @return a copy of this connections capabilities, may be empty but never null.
-     */
-    protected LinkCapabilities getLinkCapabilities(DataConnection connection) {
-        return connection.getLinkCapabilities();
-    }
-
     // tell all active apns of the current condition
     protected void notifyDataConnection(String reason) {
         for (int id = 0; id < APN_NUM_TYPES; id++) {
@@ -678,39 +753,15 @@ public abstract class DataConnectionTracker extends Handler {
     protected void notifyDataAvailability(String reason) {
         // note that we either just turned all off because we lost availability
         // or all were off and could now go on, so only have off apns to worry about
-        notifyOffApnsOfAvailability(reason, isDataPossible());
-    }
-
-    /**
-     * The only circumstances under which we report that data connectivity is not
-     * possible are
-     * <ul>
-     * <li>Data is disallowed (roaming, power state, voice call, etc).</li>
-     * <li>The current data state is {@code DISCONNECTED} for a reason other than
-     * having explicitly disabled connectivity. In other words, data is not available
-     * because the phone is out of coverage or some like reason.</li>
-     * </ul>
-     * @return {@code true} if data connectivity is possible, {@code false} otherwise.
-     */
-    protected boolean isDataPossible() {
-        boolean dataAllowed = isDataAllowed();
-        boolean anyDataEnabled = getAnyDataEnabled();
-        boolean possible = (dataAllowed
-                && !(anyDataEnabled && (mState == State.FAILED || mState == State.IDLE)));
-        if (!possible && DBG) {
-            log("isDataPossible() " + possible + ", dataAllowed=" + dataAllowed +
-                    " anyDataEnabled=" + anyDataEnabled + " dataState=" + mState);
-        }
-        return possible;
+        notifyOffApnsOfAvailability(reason, isDataPossible(Phone.APN_TYPE_DEFAULT));
     }
 
-    protected abstract boolean isDataAllowed();
-
     public boolean isApnTypeEnabled(String apnType) {
         if (apnType == null) {
-            apnType = getActiveApnType();
+            return false;
+        } else {
+            return isApnIdEnabled(apnTypeToId(apnType));
         }
-        return isApnIdEnabled(apnTypeToId(apnType));
     }
 
     protected synchronized boolean isApnIdEnabled(int id) {
@@ -790,7 +841,7 @@ public abstract class DataConnectionTracker extends Handler {
         }
     }
 
-    private void setEnabled(int id, boolean enable) {
+    protected void setEnabled(int id, boolean enable) {
         if (DBG) {
             log("setEnabled(" + id + ", " + enable + ") with old state = " + dataEnabled[id]
                     + " and enabledCount = " + enabledCount);
@@ -801,7 +852,7 @@ public abstract class DataConnectionTracker extends Handler {
         sendMessage(msg);
     }
 
-    protected synchronized void onEnableApn(int apnId, int enabled) {
+    protected void onEnableApn(int apnId, int enabled) {
         if (DBG) {
             log("EVENT_APN_ENABLE_REQUEST apnId=" + apnId + ", apnType=" + apnIdToType(apnId) +
                     ", enabled=" + enabled + ", dataEnabled = " + dataEnabled[apnId] +
@@ -809,9 +860,11 @@ public abstract class DataConnectionTracker extends Handler {
                     isApnTypeActive(apnIdToType(apnId)));
         }
         if (enabled == ENABLED) {
-            if (!dataEnabled[apnId]) {
-                dataEnabled[apnId] = true;
-                enabledCount++;
+            synchronized (this) {
+                if (!dataEnabled[apnId]) {
+                    dataEnabled[apnId] = true;
+                    enabledCount++;
+                }
             }
             String type = apnIdToType(apnId);
             if (!isApnTypeActive(type)) {
@@ -822,12 +875,16 @@ public abstract class DataConnectionTracker extends Handler {
             }
         } else {
             // disable
-            if (dataEnabled[apnId]) {
-                dataEnabled[apnId] = false;
-                enabledCount--;
-                if (enabledCount == 0) {
-                    onCleanUpConnection(true, Phone.REASON_DATA_DISABLED);
+            boolean didDisable = false;
+            synchronized (this) {
+                if (dataEnabled[apnId]) {
+                    dataEnabled[apnId] = false;
+                    enabledCount--;
+                    didDisable = true;
                 }
+            }
+            if (didDisable && enabledCount == 0) {
+                onCleanUpConnection(true, apnId, Phone.REASON_DATA_DISABLED);
 
                 // send the disconnect msg manually, since the normal route wont send
                 // it (it's not enabled)
@@ -855,6 +912,22 @@ public abstract class DataConnectionTracker extends Handler {
     }
 
     /**
+     * Called when EVENT_RESET_DONE is received so goto
+     * IDLE state and send notifications to those interested.
+     *
+     * TODO - currently unused.  Needs to be hooked into DataConnection cleanup
+     * TODO - needs to pass some notion of which connection is reset..
+     */
+    protected void onResetDone(AsyncResult ar) {
+        if (DBG) log("EVENT_RESET_DONE");
+        String reason = null;
+        if (ar.userObj instanceof String) {
+            reason = (String) ar.userObj;
+        }
+        gotoIdleAndNotifyDataConnection(reason);
+    }
+
+    /**
      * Prevent mobile data connections from being established, or once again
      * allow mobile data connections. If the state toggles, then either tear
      * down or set up data, as appropriate to match the new state.
@@ -881,19 +954,23 @@ public abstract class DataConnectionTracker extends Handler {
             }
             if (prevEnabled != getAnyDataEnabled()) {
                 if (!prevEnabled) {
-                    mRetryMgr.resetRetryCount();
+                    resetAllRetryCounts();
                     onTrySetupData(Phone.REASON_DATA_ENABLED);
                 } else {
-                    onCleanUpConnection(true, Phone.REASON_DATA_DISABLED);
+                    cleanUpAllConnections(null);
                 }
             }
         }
     }
 
-    public synchronized boolean getDataEnabled() {
-        return mDataEnabled;
+    public void cleanUpAllConnections(String cause) {
+        Message msg = obtainMessage(EVENT_CLEAN_UP_ALL_CONNECTIONS);
+        msg.obj = cause;
+        sendMessage(msg);
     }
 
+    public abstract boolean isAnyActiveDataConnections();
+
     protected void onSetDataEnabled(boolean enable) {
         boolean prevEnabled = getAnyDataEnabled();
         if (mDataEnabled != enable) {
@@ -904,12 +981,22 @@ public abstract class DataConnectionTracker extends Handler {
                     Settings.Secure.MOBILE_DATA, enable ? 1 : 0);
             if (prevEnabled != getAnyDataEnabled()) {
                 if (!prevEnabled) {
-                    mRetryMgr.resetRetryCount();
+                    resetAllRetryCounts();
                     onTrySetupData(Phone.REASON_DATA_ENABLED);
                 } else {
-                    onCleanUpConnection(true, Phone.REASON_DATA_DISABLED);
+                    onCleanUpAllConnections(Phone.REASON_DATA_DISABLED);
                 }
             }
         }
     }
+
+    protected void onSetDependencyMet(String apnType, boolean met) {
+    }
+
+
+    protected void resetAllRetryCounts() {
+        for (DataConnection dc : mDataConnections.values()) {
+            dc.resetRetryCount();
+        }
+    }
 }
index 83db3d1..aa7568b 100644 (file)
@@ -21,6 +21,7 @@ import android.net.LinkProperties;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
@@ -55,8 +56,13 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
     }
 
     public void notifyServiceState(Phone sender) {
+        ServiceState ss = sender.getServiceState();
+        if (ss == null) {
+            ss = new ServiceState();
+            ss.setStateOutOfService();
+        }
         try {
-            mRegistry.notifyServiceState(sender.getServiceState());
+            mRegistry.notifyServiceState(ss);
         } catch (RemoteException ex) {
             // system process is dead
         }
@@ -114,8 +120,8 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
         try {
             mRegistry.notifyDataConnection(
                     convertDataState(state),
-                    sender.isDataConnectivityPossible(), reason,
-                    sender.getActiveApnHost(),
+                    sender.isDataConnectivityPossible(apnType), reason,
+                    sender.getActiveApnHost(apnType),
                     apnType,
                     linkProperties,
                     linkCapabilities,
index 3c4bb12..19441cd 100644 (file)
@@ -145,6 +145,15 @@ interface ITelephony {
     boolean supplyPin(String pin);
 
     /**
+     * Supply puk to unlock the SIM and set SIM pin to new pin.
+     *  Blocks until a result is determined.
+     * @param puk The puk to check.
+     *        pin The new pin to be set in SIM
+     * @return whether the operation was a success.
+     */
+    boolean supplyPuk(String puk, String pin);
+
+    /**
      * Handles PIN MMI commands (PIN/PIN2/PUK/PUK2), which are initiated
      * without SEND (so <code>dial</code> is not appropriate).
      *
@@ -254,10 +263,20 @@ interface ITelephony {
       * Returns the network type
       */
     int getNetworkType();
-    
+
     /**
      * Return true if an ICC card is present
      */
     boolean hasIccCard();
+
+    /**
+     * Return if the current radio is LTE on CDMA. This
+     * is a tri-state return value as for a period of time
+     * the mode may be unknown.
+     *
+     * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
+     * or {@link PHone#LTE_ON_CDMA_TRUE}
+     */
+    int getLteOnCdmaMode();
 }
 
index e270ce9..02617c8 100644 (file)
@@ -18,16 +18,28 @@ package com.android.internal.telephony;
 
 import static android.Manifest.permission.READ_PHONE_STATE;
 import android.app.ActivityManagerNative;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
+import android.os.Power;
+import android.os.PowerManager;
 import android.os.Registrant;
 import android.os.RegistrantList;
 import android.util.Log;
+import android.view.WindowManager;
 
 import com.android.internal.telephony.PhoneBase;
 import com.android.internal.telephony.CommandsInterface.RadioState;
+import com.android.internal.telephony.gsm.SIMRecords;
+
+import android.os.SystemProperties;
+
+import com.android.internal.R;
 
 /**
  * {@hide}
@@ -72,6 +84,9 @@ public abstract class IccCard {
     static public final String INTENT_VALUE_LOCKED_ON_PUK = "PUK";
     /* NETWORK means ICC is locked on NETWORK PERSONALIZATION */
     static public final String INTENT_VALUE_LOCKED_NETWORK = "NETWORK";
+    /* PERM_DISABLED means ICC is permanently disabled due to puk fails */
+    static public final String INTENT_VALUE_ABSENT_ON_PERM_DISABLED = "PERM_DISABLED";
+
 
     protected static final int EVENT_ICC_LOCKED_OR_ABSENT = 1;
     private static final int EVENT_GET_ICC_STATUS_DONE = 2;
@@ -84,6 +99,9 @@ public abstract class IccCard {
     private static final int EVENT_CHANGE_ICC_PASSWORD_DONE = 9;
     private static final int EVENT_QUERY_FACILITY_FDN_DONE = 10;
     private static final int EVENT_CHANGE_FACILITY_FDN_DONE = 11;
+    private static final int EVENT_ICC_STATUS_CHANGED = 12;
+    private static final int EVENT_CARD_REMOVED = 13;
+    private static final int EVENT_CARD_ADDED = 14;
 
     /*
       UNKNOWN is a transient state, for example, after uesr inputs ICC pin under
@@ -97,11 +115,18 @@ public abstract class IccCard {
         PUK_REQUIRED,
         NETWORK_LOCKED,
         READY,
-        NOT_READY;
+        NOT_READY,
+        PERM_DISABLED;
 
         public boolean isPinLocked() {
             return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED));
         }
+
+        public boolean iccCardExist() {
+            return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED)
+                    || (this == NETWORK_LOCKED) || (this == READY)
+                    || (this == PERM_DISABLED));
+        }
     }
 
     public State getState() {
@@ -137,11 +162,14 @@ public abstract class IccCard {
 
     public IccCard(PhoneBase phone, String logTag, Boolean dbg) {
         mPhone = phone;
+        mPhone.mCM.registerForIccStatusChanged(mHandler, EVENT_ICC_STATUS_CHANGED, null);
         mLogTag = logTag;
         mDbg = dbg;
     }
 
-    abstract public void dispose();
+    public void dispose() {
+        mPhone.mCM.unregisterForIccStatusChanged(mHandler);
+    }
 
     protected void finalize() {
         if(mDbg) Log.d(mLogTag, "IccCard finalized");
@@ -393,6 +421,9 @@ public abstract class IccCard {
         boolean transitionedIntoPinLocked;
         boolean transitionedIntoAbsent;
         boolean transitionedIntoNetworkLocked;
+        boolean transitionedIntoPermBlocked;
+        boolean isIccCardRemoved;
+        boolean isIccCardAdded;
 
         State oldState, newState;
 
@@ -409,23 +440,93 @@ public abstract class IccCard {
         transitionedIntoAbsent = (oldState != State.ABSENT && newState == State.ABSENT);
         transitionedIntoNetworkLocked = (oldState != State.NETWORK_LOCKED
                 && newState == State.NETWORK_LOCKED);
+        transitionedIntoPermBlocked = (oldState != State.PERM_DISABLED
+                && newState == State.PERM_DISABLED);
+        isIccCardRemoved = (oldState != null &&
+                        oldState.iccCardExist() && newState == State.ABSENT);
+        isIccCardAdded = (oldState == State.ABSENT &&
+                        newState != null && newState.iccCardExist());
 
         if (transitionedIntoPinLocked) {
-            if(mDbg) log("Notify SIM pin or puk locked.");
+            if (mDbg) log("Notify SIM pin or puk locked.");
             mPinLockedRegistrants.notifyRegistrants();
             broadcastIccStateChangedIntent(INTENT_VALUE_ICC_LOCKED,
                     (newState == State.PIN_REQUIRED) ?
                        INTENT_VALUE_LOCKED_ON_PIN : INTENT_VALUE_LOCKED_ON_PUK);
         } else if (transitionedIntoAbsent) {
-            if(mDbg) log("Notify SIM missing.");
+            if (mDbg) log("Notify SIM missing.");
             mAbsentRegistrants.notifyRegistrants();
             broadcastIccStateChangedIntent(INTENT_VALUE_ICC_ABSENT, null);
         } else if (transitionedIntoNetworkLocked) {
-            if(mDbg) log("Notify SIM network locked.");
+            if (mDbg) log("Notify SIM network locked.");
             mNetworkLockedRegistrants.notifyRegistrants();
             broadcastIccStateChangedIntent(INTENT_VALUE_ICC_LOCKED,
                   INTENT_VALUE_LOCKED_NETWORK);
+        } else if (transitionedIntoPermBlocked) {
+            if (mDbg) log("Notify SIM permanently disabled.");
+            broadcastIccStateChangedIntent(INTENT_VALUE_ICC_ABSENT,
+                    INTENT_VALUE_ABSENT_ON_PERM_DISABLED);
+        }
+
+        if (isIccCardRemoved) {
+            mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_REMOVED, null));
+        } else if (isIccCardAdded) {
+            mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_ADDED, null));
         }
+
+
+
+        /*
+         * TODO: We need to try to remove this, maybe if the RIL sends up a RIL_UNSOL_SIM_REFRESH?
+         */
+        if (oldState != State.READY && newState == State.READY &&
+                mPhone.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE) {
+            if (mPhone.mIccRecords instanceof SIMRecords) {
+                ((SIMRecords)mPhone.mIccRecords).onSimReady();
+            }
+        }
+
+    }
+
+    private void onIccSwap(boolean isAdded) {
+        // TODO: Here we assume the device can't handle SIM hot-swap
+        //      and has to reboot. We may want to add a property,
+        //      e.g. REBOOT_ON_SIM_SWAP, to indicate if modem support
+        //      hot-swap.
+        DialogInterface.OnClickListener listener = null;
+
+
+        // TODO: SimRecords is not reset while SIM ABSENT (only reset while
+        //       Radio_off_or_not_available). Have to reset in both both
+        //       added or removed situation.
+        listener = new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                if (which == DialogInterface.BUTTON_POSITIVE) {
+                    if (mDbg) log("Reboot due to SIM swap");
+                    PowerManager pm = (PowerManager) mPhone.getContext()
+                    .getSystemService(Context.POWER_SERVICE);
+                    pm.reboot("SIM is added.");
+                }
+            }
+
+        };
+
+        Resources r = Resources.getSystem();
+
+        String title = (isAdded) ? r.getString(R.string.sim_added_title) :
+            r.getString(R.string.sim_removed_title);
+        String message = (isAdded) ? r.getString(R.string.sim_added_message) :
+            r.getString(R.string.sim_removed_message);
+        String buttonTxt = r.getString(R.string.sim_restart_button);
+
+        AlertDialog dialog = new AlertDialog.Builder(mPhone.getContext())
+            .setTitle(title)
+            .setMessage(message)
+            .setPositiveButton(buttonTxt, listener)
+            .create();
+        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+        dialog.show();
     }
 
     /**
@@ -588,6 +689,16 @@ public abstract class IccCard {
                                                         = ar.exception;
                     ((Message)ar.userObj).sendToTarget();
                     break;
+                case EVENT_ICC_STATUS_CHANGED:
+                    Log.d(mLogTag, "Received Event EVENT_ICC_STATUS_CHANGED");
+                    mPhone.mCM.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
+                    break;
+                case EVENT_CARD_REMOVED:
+                    onIccSwap(false);
+                    break;
+                case EVENT_CARD_ADDED:
+                    onIccSwap(true);
+                    break;
                 default:
                     Log.e(mLogTag, "[IccCard] Unknown Event " + msg.what);
             }
@@ -612,61 +723,102 @@ public abstract class IccCard {
             currentRadioState == RadioState.SIM_NOT_READY     ||
             currentRadioState == RadioState.RUIM_NOT_READY    ||
             currentRadioState == RadioState.NV_NOT_READY      ||
-            currentRadioState == RadioState.NV_READY) {
+            (currentRadioState == RadioState.NV_READY &&
+                    (mPhone.getLteOnCdmaMode() != Phone.LTE_ON_CDMA_TRUE))) {
             return IccCard.State.NOT_READY;
         }
 
         if( currentRadioState == RadioState.SIM_LOCKED_OR_ABSENT  ||
             currentRadioState == RadioState.SIM_READY             ||
             currentRadioState == RadioState.RUIM_LOCKED_OR_ABSENT ||
-            currentRadioState == RadioState.RUIM_READY) {
+            currentRadioState == RadioState.RUIM_READY ||
+            (currentRadioState == RadioState.NV_READY &&
+                    (mPhone.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE))) {
 
-            int index;
+            State csimState =
+                getAppState(mIccCardStatus.getCdmaSubscriptionAppIndex());
+            State usimState =
+                getAppState(mIccCardStatus.getGsmUmtsSubscriptionAppIndex());
+
+            if(mDbg) log("USIM=" + usimState + " CSIM=" + csimState);
+
+            if (mPhone.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE) {
+                // UICC card contains both USIM and CSIM
+                // Return consolidated status
+                return getConsolidatedState(csimState, usimState, csimState);
+            }
 
             // check for CDMA radio technology
             if (currentRadioState == RadioState.RUIM_LOCKED_OR_ABSENT ||
                 currentRadioState == RadioState.RUIM_READY) {
-                index = mIccCardStatus.getCdmaSubscriptionAppIndex();
-            }
-            else {
-                index = mIccCardStatus.getGsmUmtsSubscriptionAppIndex();
+                return csimState;
             }
+            return usimState;
+        }
 
-            IccCardApplication app;
-            if (index >= 0 && index < IccCardStatus.CARD_MAX_APPS) {
-                app = mIccCardStatus.getApplication(index);
-            } else {
-                Log.e(mLogTag, "[IccCard] Invalid Subscription Application index:" + index);
-                return IccCard.State.ABSENT;
-            }
+        return IccCard.State.ABSENT;
+    }
 
-            if (app == null) {
-                Log.e(mLogTag, "[IccCard] Subscription Application in not present");
-                return IccCard.State.ABSENT;
-            }
+    private State getAppState(int appIndex) {
+        IccCardApplication app;
+        if (appIndex >= 0 && appIndex < IccCardStatus.CARD_MAX_APPS) {
+            app = mIccCardStatus.getApplication(appIndex);
+        } else {
+            Log.e(mLogTag, "[IccCard] Invalid Subscription Application index:" + appIndex);
+            return IccCard.State.ABSENT;
+        }
 
-            // check if PIN required
-            if (app.app_state.isPinRequired()) {
-                return IccCard.State.PIN_REQUIRED;
-            }
-            if (app.app_state.isPukRequired()) {
-                return IccCard.State.PUK_REQUIRED;
-            }
-            if (app.app_state.isSubscriptionPersoEnabled()) {
-                return IccCard.State.NETWORK_LOCKED;
-            }
-            if (app.app_state.isAppReady()) {
-                return IccCard.State.READY;
-            }
-            if (app.app_state.isAppNotReady()) {
-                return IccCard.State.NOT_READY;
-            }
-            return IccCard.State.NOT_READY;
+        if (app == null) {
+            Log.e(mLogTag, "[IccCard] Subscription Application in not present");
+            return IccCard.State.ABSENT;
         }
 
-        return IccCard.State.ABSENT;
+        // check if PIN required
+        if (app.pin1.isPermBlocked()) {
+            return IccCard.State.PERM_DISABLED;
+        }
+        if (app.app_state.isPinRequired()) {
+            return IccCard.State.PIN_REQUIRED;
+        }
+        if (app.app_state.isPukRequired()) {
+            return IccCard.State.PUK_REQUIRED;
+        }
+        if (app.app_state.isSubscriptionPersoEnabled()) {
+            return IccCard.State.NETWORK_LOCKED;
+        }
+        if (app.app_state.isAppReady()) {
+            return IccCard.State.READY;
+        }
+        if (app.app_state.isAppNotReady()) {
+            return IccCard.State.NOT_READY;
+        }
+        return IccCard.State.NOT_READY;
     }
 
+    private State getConsolidatedState(State left, State right, State preferredState) {
+        // Check if either is absent.
+        if (right == IccCard.State.ABSENT) return left;
+        if (left == IccCard.State.ABSENT) return right;
+
+        // Only if both are ready, return ready
+        if ((left == IccCard.State.READY) && (right == IccCard.State.READY)) {
+            return State.READY;
+        }
+
+        // Case one is ready, but the other is not.
+        if (((right == IccCard.State.NOT_READY) && (left == IccCard.State.READY)) ||
+            ((left == IccCard.State.NOT_READY) && (right == IccCard.State.READY))) {
+            return IccCard.State.NOT_READY;
+        }
+
+        // At this point, the other state is assumed to be one of locked state
+        if (right == IccCard.State.NOT_READY) return left;
+        if (left == IccCard.State.NOT_READY) return right;
+
+        // At this point, FW currently just assumes the status will be
+        // consistent across the applications...
+        return preferredState;
+    }
 
     public boolean isApplicationOnIcc(IccCardApplication.AppType type) {
         if (mIccCardStatus == null) return false;
index 9f60a6c..abb740e 100644 (file)
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import com.android.internal.telephony.IccCardStatus.PinState;
+
 
 /**
  * See also RIL_AppStatus in include/telephony/ril.h
@@ -28,7 +30,8 @@ public class IccCardApplication {
         APPTYPE_SIM,
         APPTYPE_USIM,
         APPTYPE_RUIM,
-        APPTYPE_CSIM
+        APPTYPE_CSIM,
+        APPTYPE_ISIM
     };
 
     public enum AppState{
@@ -103,8 +106,8 @@ public class IccCardApplication {
     public String         app_label;
     // applicable to USIM and CSIM
     public int            pin1_replaced;
-    public int            pin1;
-    public int            pin2;
+    public PinState            pin1;
+    public PinState            pin2;
 
     AppType AppTypeFromRILInt(int type) {
         AppType newType;
@@ -115,6 +118,7 @@ public class IccCardApplication {
             case 2: newType = AppType.APPTYPE_USIM;    break;
             case 3: newType = AppType.APPTYPE_RUIM;    break;
             case 4: newType = AppType.APPTYPE_CSIM;    break;
+            case 5: newType = AppType.APPTYPE_ISIM;    break;
             default:
                 throw new RuntimeException(
                             "Unrecognized RIL_AppType: " +type);
@@ -175,4 +179,48 @@ public class IccCardApplication {
         return newSubState;
     }
 
+    PinState PinStateFromRILInt(int state) {
+        PinState newPinState;
+        switch(state) {
+            case 0:
+                newPinState = PinState.PINSTATE_UNKNOWN;
+                break;
+            case 1:
+                newPinState = PinState.PINSTATE_ENABLED_NOT_VERIFIED;
+                break;
+            case 2:
+                newPinState = PinState.PINSTATE_ENABLED_VERIFIED;
+                break;
+            case 3:
+                newPinState = PinState.PINSTATE_DISABLED;
+                break;
+            case 4:
+                newPinState = PinState.PINSTATE_ENABLED_BLOCKED;
+                break;
+            case 5:
+                newPinState = PinState.PINSTATE_ENABLED_PERM_BLOCKED;
+                break;
+            default:
+                throw new RuntimeException("Unrecognized RIL_PinState: " + state);
+        }
+        return newPinState;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("{").append(app_type).append(",").append(app_state);
+        if (app_state == AppState.APPSTATE_SUBSCRIPTION_PERSO) {
+            sb.append(",").append(perso_substate);
+        }
+        if (app_type == AppType.APPTYPE_CSIM ||
+                app_type == AppType.APPTYPE_USIM ||
+                app_type == AppType.APPTYPE_ISIM) {
+            sb.append(",pin1=").append(pin1);
+            sb.append(",pin2=").append(pin2);
+        }
+        sb.append("}");
+        return sb.toString();
+    }
 }
index 7199616..c751a21 100644 (file)
@@ -42,7 +42,19 @@ public class IccCardStatus {
         PINSTATE_ENABLED_VERIFIED,
         PINSTATE_DISABLED,
         PINSTATE_ENABLED_BLOCKED,
-        PINSTATE_ENABLED_PERM_BLOCKED
+        PINSTATE_ENABLED_PERM_BLOCKED;
+
+        boolean isPermBlocked() {
+            return this == PINSTATE_ENABLED_PERM_BLOCKED;
+        }
+
+        boolean isPinRequired() {
+            return this == PINSTATE_ENABLED_NOT_VERIFIED;
+        }
+
+        boolean isPukRequired() {
+            return this == PINSTATE_ENABLED_BLOCKED;
+        }
     }
 
     private CardState  mCardState;
@@ -144,4 +156,33 @@ public class IccCardStatus {
         return mApplications.get(index);
     }
 
+    @Override
+    public String toString() {
+        IccCardApplication app;
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("IccCardState {").append(mCardState).append(",")
+        .append(mUniversalPinState)
+        .append(",num_apps=").append(mNumApplications)
+        .append(",gsm_id=").append(mGsmUmtsSubscriptionAppIndex);
+        if (mGsmUmtsSubscriptionAppIndex >=0
+                && mGsmUmtsSubscriptionAppIndex <CARD_MAX_APPS) {
+            app = getApplication(mGsmUmtsSubscriptionAppIndex);
+            sb.append(app == null ? "null" : app);
+        }
+
+        sb.append(",cmda_id=").append(mCdmaSubscriptionAppIndex);
+        if (mCdmaSubscriptionAppIndex >=0
+                && mCdmaSubscriptionAppIndex <CARD_MAX_APPS) {
+            app = getApplication(mCdmaSubscriptionAppIndex);
+            sb.append(app == null ? "null" : app);
+        }
+
+        sb.append(",ism_id=").append(mImsSubscriptionAppIndex);
+
+        sb.append("}");
+
+        return sb.toString();
+    }
+
 }
index b12d2d4..1ba6dfe 100644 (file)
@@ -58,6 +58,23 @@ public interface IccConstants {
     static final int EF_CST = 0x6f32;
     static final int EF_RUIM_SPN =0x6F41;
 
+    // ETSI TS.102.221
+    static final int EF_PL = 0x2F05;
+    // 3GPP2 C.S0065
+    static final int EF_CSIM_LI = 0x6F3A;
+    static final int EF_CSIM_SPN =0x6F41;
+    static final int EF_CSIM_MDN = 0x6F44;
+    static final int EF_CSIM_IMSIM = 0x6F22;
+    static final int EF_CSIM_CDMAHOME = 0x6F28;
+    static final int EF_CSIM_EPRL = 0x6F5A;
+
+    //ISIM access
+    static final int EF_IMPU = 0x6f04;
+    static final int EF_IMPI = 0x6f02;
+    static final int EF_DOMAIN = 0x6f03;
+    static final int EF_IST = 0x6f07;
+    static final int EF_PCSCF = 0x6f09;
+
     // SMS record length from TS 51.011 10.5.3
     static public final int SMS_RECORD_LENGTH = 176;
 
@@ -67,4 +84,7 @@ public interface IccConstants {
     static final String DF_GRAPHICS = "5F50";
     static final String DF_GSM = "7F20";
     static final String DF_CDMA = "7F25";
+
+    //ISIM access
+    static final String DF_ADFISIM = "7FFF";
 }
index 92ddd2c..93b9b79 100644 (file)
@@ -529,6 +529,7 @@ public abstract class IccFileHandler extends Handler implements IccConstants {
             return MF_SIM + DF_TELECOM;
 
         case EF_ICCID:
+        case EF_PL:
             return MF_SIM;
         case EF_IMG:
             return MF_SIM + DF_TELECOM + DF_GRAPHICS;
index b8d9e3c..3a27901 100644 (file)
@@ -21,9 +21,6 @@ import android.os.Handler;
 import android.os.Message;
 import android.os.Registrant;
 import android.os.RegistrantList;
-import android.util.Log;
-
-import java.util.ArrayList;
 
 /**
  * {@hide}
@@ -79,6 +76,11 @@ public abstract class IccRecords extends Handler implements IccConstants {
         this.phone = p;
     }
 
+    /**
+     * Call when the IccRecords object is no longer going to be used.
+     */
+    public abstract void dispose();
+
     protected abstract void onRadioOffOrNotAvailable();
 
     //***** Public Methods
@@ -99,6 +101,17 @@ public abstract class IccRecords extends Handler implements IccConstants {
         recordsLoadedRegistrants.remove(h);
     }
 
+    /**
+     * Get the International Mobile Subscriber ID (IMSI) on a SIM
+     * for GSM, UMTS and like networks. Default is null if IMSI is
+     * not supported or unavailable.
+     *
+     * @return null if SIM is not yet ready or unavailable
+     */
+    public String getIMSI() {
+        return null;
+    }
+
     public String getMsisdnNumber() {
         return msisdn;
     }
@@ -220,6 +233,7 @@ public abstract class IccRecords extends Handler implements IccConstants {
     }
 
     //***** Overridden from Handler
+    @Override
     public abstract void handleMessage(Message msg);
 
     protected abstract void onRecordLoaded();
@@ -232,8 +246,61 @@ public abstract class IccRecords extends Handler implements IccConstants {
      * and TS 51.011 10.3.11 for details.
      *
      * If the SPN is not found on the SIM, the rule is always PLMN_ONLY.
+     * Generally used for GSM/UMTS and the like SIMs.
      */
-    protected abstract int getDisplayRule(String plmn);
+    public abstract int getDisplayRule(String plmn);
 
+    /**
+     * Return true if "Restriction of menu options for manual PLMN selection"
+     * bit is set or EF_CSP data is unavailable, return false otherwise.
+     * Generally used for GSM/UMTS and the like SIMs.
+     */
+    public boolean isCspPlmnEnabled() {
+        return false;
+    }
+
+    /**
+     * Returns the 5 or 6 digit MCC/MNC of the operator that
+     * provided the SIM card. Returns null of SIM is not yet ready
+     * or is not valid for the type of IccCard. Generally used for
+     * GSM/UMTS and the like SIMS
+     */
+    public String getOperatorNumeric() {
+        return null;
+    }
+
+    /**
+     * Get the current Voice call forwarding flag for GSM/UMTS and the like SIMs
+     *
+     * @return true if enabled
+     */
+    public boolean getVoiceCallForwardingFlag() {
+        return false;
+    }
+
+    /**
+     * Set the voice call forwarding flag for GSM/UMTS and the like SIMs
+     *
+     * @param line to enable/disable
+     * @param enable
+     */
+    public void setVoiceCallForwardingFlag(int line, boolean enable) {
+    }
+
+    /**
+     * Indicates wether SIM is in provisioned state or not.
+     * Overridden only if SIM can be dynamically provisioned via OTA.
+     *
+     * @return true if provisioned
+     */
+    public boolean isProvisioned () {
+        return true;
+    }
+
+    /**
+     * Write string to log file
+     *
+     * @param s is the string to write
+     */
     protected abstract void log(String s);
 }
index df579b0..8e60e6e 100644 (file)
@@ -63,6 +63,29 @@ public class IccUtils {
         return ret.toString();
     }
 
+    /**
+     * Decode cdma byte into String.
+     */
+    public static String
+    cdmaBcdToString(byte[] data, int offset, int length) {
+        StringBuilder ret = new StringBuilder(length);
+
+        int count = 0;
+        for (int i = offset; count < length; i++) {
+            int v;
+            v = data[i] & 0xf;
+            if (v > 9)  v = 0;
+            ret.append((char)('0' + v));
+
+            if (++count == length) break;
+
+            v = (data[i] >> 4) & 0xf;
+            if (v > 9)  v = 0;
+            ret.append((char)('0' + v));
+            ++count;
+        }
+        return ret.toString();
+    }
 
     /**
      * Decodes a GSM-style BCD byte, returning an int ranging from 0-99.
index fde1b59..cdce841 100644 (file)
@@ -234,7 +234,7 @@ public final class MccTable
         String country = MccTable.countryCodeForMcc(mcc);
 
         Log.d(LOG_TAG, "locale set to "+language+"_"+country);
-        phone.setSystemLocale(language, country);
+        phone.setSystemLocale(language, country, true);
     }
 
     /**
index acb86d4..48c5318 100644 (file)
@@ -21,6 +21,7 @@ import android.net.LinkCapabilities;
 import android.net.LinkProperties;
 import android.os.Handler;
 import android.os.Message;
+import android.os.SystemProperties;
 import android.telephony.CellLocation;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
@@ -125,6 +126,12 @@ public interface Phone {
     static final String APN_TYPE_DUN = "dun";
     /** APN type for HiPri traffic */
     static final String APN_TYPE_HIPRI = "hipri";
+    /** APN type for FOTA */
+    static final String APN_TYPE_FOTA = "fota";
+    /** APN type for IMS */
+    static final String APN_TYPE_IMS = "ims";
+    /** APN type for CBS */
+    static final String APN_TYPE_CBS = "cbs";
 
     // "Features" accessible through the connectivity manager
     static final String FEATURE_ENABLE_MMS = "enableMMS";
@@ -132,6 +139,9 @@ public interface Phone {
     static final String FEATURE_ENABLE_DUN = "enableDUN";
     static final String FEATURE_ENABLE_HIPRI = "enableHIPRI";
     static final String FEATURE_ENABLE_DUN_ALWAYS = "enableDUNAlways";
+    static final String FEATURE_ENABLE_FOTA = "enableFOTA";
+    static final String FEATURE_ENABLE_IMS = "enableIMS";
+    static final String FEATURE_ENABLE_CBS = "enableCBS";
 
     /**
      * Return codes for <code>enableApnType()</code>
@@ -140,6 +150,7 @@ public interface Phone {
     static final int APN_REQUEST_STARTED    = 1;
     static final int APN_TYPE_NOT_AVAILABLE = 2;
     static final int APN_REQUEST_FAILED     = 3;
+    static final int APN_ALREADY_INACTIVE   = 4;
 
 
     /**
@@ -149,8 +160,8 @@ public interface Phone {
     static final String REASON_ROAMING_OFF = "roamingOff";
     static final String REASON_DATA_DISABLED = "dataDisabled";
     static final String REASON_DATA_ENABLED = "dataEnabled";
-    static final String REASON_GPRS_ATTACHED = "gprsAttached";
-    static final String REASON_GPRS_DETACHED = "gprsDetached";
+    static final String REASON_DATA_ATTACHED = "dataAttached";
+    static final String REASON_DATA_DETACHED = "dataDetached";
     static final String REASON_CDMA_DATA_ATTACHED = "cdmaDataAttached";
     static final String REASON_CDMA_DATA_DETACHED = "cdmaDataDetached";
     static final String REASON_APN_CHANGED = "apnChanged";
@@ -164,6 +175,10 @@ public interface Phone {
     static final String REASON_PS_RESTRICT_ENABLED = "psRestrictEnabled";
     static final String REASON_PS_RESTRICT_DISABLED = "psRestrictDisabled";
     static final String REASON_SIM_LOADED = "simLoaded";
+    static final String REASON_NW_TYPE_CHANGED = "nwTypeChanged";
+    static final String REASON_DATA_DEPENDENCY_MET = "dependencyMet";
+    static final String REASON_DATA_DEPENDENCY_UNMET = "dependencyUnmet";
+    static final String REASON_LINK_PROPERTIES_CHANGED = "linkPropertiesChanged";
 
     // Used for band mode selection methods
     static final int BM_UNSPECIFIED = 0; // selected by baseband automatically
@@ -180,6 +195,11 @@ public interface Phone {
     static final int PHONE_TYPE_CDMA = RILConstants.CDMA_PHONE;
     static final int PHONE_TYPE_SIP = RILConstants.SIP_PHONE;
 
+    // Modes for LTE_ON_CDMA
+    static final int LTE_ON_CDMA_UNKNOWN = RILConstants.LTE_ON_CDMA_UNKNOWN;
+    static final int LTE_ON_CDMA_FALSE = RILConstants.LTE_ON_CDMA_FALSE;
+    static final int LTE_ON_CDMA_TRUE = RILConstants.LTE_ON_CDMA_TRUE;
+
     // Used for preferred network type
     // Note NT_* substitute RILConstants.NETWORK_MODE_* above the Phone
     int NT_MODE_WCDMA_PREF   = RILConstants.NETWORK_MODE_WCDMA_PREF;
@@ -193,6 +213,7 @@ public interface Phone {
     int NT_MODE_EVDO_NO_CDMA = RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
     int NT_MODE_GLOBAL       = RILConstants.NETWORK_MODE_GLOBAL;
 
+    int NT_MODE_LTE_ONLY     = RILConstants.NETWORK_MODE_LTE_ONLY;
     int PREFERRED_NT_MODE    = RILConstants.PREFERRED_NETWORK_MODE;
 
 
@@ -319,7 +340,7 @@ public interface Phone {
      * Returns string for the active APN host.
      *  @return type as a string or null if none.
      */
-    String getActiveApnHost();
+    String getActiveApnHost(String apnType);
 
     /**
      * Return the LinkProperties for the named apn or null if not available
@@ -1236,13 +1257,6 @@ public interface Phone {
     void getDataCallList(Message response);
 
     /**
-     * Get current mutiple data connection status
-     *
-     * @return list of data connections
-     */
-    List<DataConnection> getCurrentDataConnectionList();
-
-    /**
      * Update the ServiceState CellLocation for current network registration.
      */
     void updateServiceLocation();
@@ -1361,6 +1375,11 @@ public interface Phone {
     boolean isDataConnectivityPossible();
 
     /**
+     * Report on whether data connectivity is allowed for an APN.
+     */
+    boolean isDataConnectivityPossible(String apnType);
+
+    /**
      * Retrieves the unique device ID, e.g., IMEI for GSM phones and MEID for CDMA phones.
      */
     String getDeviceId();
@@ -1686,6 +1705,14 @@ public interface Phone {
      */
     void unsetOnEcbModeExitResponse(Handler h);
 
+    /**
+     * Return if the current radio is LTE on CDMA. This
+     * is a tri-state return value as for a period of time
+     * the mode may be unknown.
+     *
+     * @return {@link #LTE_ON_CDMA_UNKNOWN}, {@link #LTE_ON_CDMA_FALSE} or {@link #LTE_ON_CDMA_TRUE}
+     */
+    public int getLteOnCdmaMode();
 
     /**
      * TODO: Adding a function for each property is not good.
index 54341b1..40a70a8 100644 (file)
@@ -38,6 +38,8 @@ import android.util.Log;
 
 import com.android.internal.R;
 import com.android.internal.telephony.test.SimulatedRadioControl;
+import com.android.internal.telephony.gsm.SIMRecords;
+import com.android.internal.telephony.gsm.SimCard;
 
 import java.util.Locale;
 
@@ -110,12 +112,15 @@ public abstract class PhoneBase extends Handler implements Phone {
     public CommandsInterface mCM;
     protected IccFileHandler mIccFileHandler;
     boolean mDnsCheckDisabled = false;
-    public DataConnectionTracker mDataConnection;
+    public DataConnectionTracker mDataConnectionTracker;
     boolean mDoesRilSendMultipleCallRing;
     int mCallRingContinueToken = 0;
     int mCallRingDelay;
     public boolean mIsTheCurrentActivePhone = true;
     boolean mIsVoiceCapable = true;
+    public IccRecords mIccRecords;
+    public IccCard mIccCard;
+    public SMSDispatcher mSMS;
 
     /**
      * Set a system property, unless we're in unit test mode
@@ -237,7 +242,8 @@ public abstract class PhoneBase extends Handler implements Phone {
     public void dispose() {
         synchronized(PhoneProxy.lockForRadioTechnologyChange) {
             mCM.unSetOnCallRing(this);
-            mDataConnection.onCleanUpConnection(false, REASON_RADIO_TURNED_OFF);
+            // Must cleanup all connectionS and needs to use sendMessage!
+            mDataConnectionTracker.cleanUpAllConnections(null);
             mIsTheCurrentActivePhone = false;
         }
     }
@@ -574,7 +580,7 @@ public abstract class PhoneBase extends Handler implements Phone {
                 if (l.length() >=5) {
                     country = l.substring(3, 5);
                 }
-                setSystemLocale(language, country);
+                setSystemLocale(language, country, false);
 
                 if (!country.isEmpty()) {
                     try {
@@ -596,10 +602,14 @@ public abstract class PhoneBase extends Handler implements Phone {
      * Utility code to set the system locale if it's not set already
      * @param language Two character language code desired
      * @param country Two character country code desired
+     * @param fromMcc Indicating whether the locale is set according to MCC table.
+     *                This flag wil be ignored by default implementation.
+     *                TODO: Use a source enumeration so that source of the locale
+     *                      can be prioritized.
      *
      *  {@hide}
      */
-    public void setSystemLocale(String language, String country) {
+    public void setSystemLocale(String language, String country, boolean fromMcc) {
         String l = SystemProperties.get("persist.sys.language");
         String c = SystemProperties.get("persist.sys.country");
 
@@ -662,6 +672,45 @@ public abstract class PhoneBase extends Handler implements Phone {
     }
 
     /**
+    * Retrieves the ServiceStateTracker of the phone instance.
+    */
+    public ServiceStateTracker getServiceStateTracker() {
+        return null;
+    }
+
+    /**
+    * Get call tracker
+    */
+    public CallTracker getCallTracker() {
+        return null;
+    }
+
+    @Override
+    public IccCard getIccCard() {
+        return mIccCard;
+    }
+
+    @Override
+    public String getIccSerialNumber() {
+        return mIccRecords.iccid;
+    }
+
+    @Override
+    public boolean getIccRecordsLoaded() {
+        return mIccRecords.getRecordsLoaded();
+    }
+
+    @Override
+    public boolean getMessageWaitingIndicator() {
+        return mIccRecords.getVoiceMessageWaiting();
+    }
+
+    @Override
+    public boolean getCallForwardingIndicator() {
+        return mIccRecords.getVoiceCallForwardingFlag();
+    }
+
+    /**
      *  Query the status of the CDMA roaming preference
      */
     public void queryCdmaRoamingPreference(Message response) {
@@ -954,31 +1003,36 @@ public abstract class PhoneBase extends Handler implements Phone {
      }
 
     public String[] getActiveApnTypes() {
-        return mDataConnection.getActiveApnTypes();
+        return mDataConnectionTracker.getActiveApnTypes();
     }
 
-    public String getActiveApnHost() {
-        return mDataConnection.getActiveApnString();
+    public String getActiveApnHost(String apnType) {
+        return mDataConnectionTracker.getActiveApnString(apnType);
     }
 
     public LinkProperties getLinkProperties(String apnType) {
-        return mDataConnection.getLinkProperties(apnType);
+        return mDataConnectionTracker.getLinkProperties(apnType);
     }
 
     public LinkCapabilities getLinkCapabilities(String apnType) {
-        return mDataConnection.getLinkCapabilities(apnType);
+        return mDataConnectionTracker.getLinkCapabilities(apnType);
     }
 
     public int enableApnType(String type) {
-        return mDataConnection.enableApnType(type);
+        return mDataConnectionTracker.enableApnType(type);
     }
 
     public int disableApnType(String type) {
-        return mDataConnection.disableApnType(type);
+        return mDataConnectionTracker.disableApnType(type);
     }
 
     public boolean isDataConnectivityPossible() {
-        return ((mDataConnection != null) && (mDataConnection.isDataPossible()));
+        return isDataConnectivityPossible(Phone.APN_TYPE_DEFAULT);
+    }
+
+    public boolean isDataConnectivityPossible(String apnType) {
+        return ((mDataConnectionTracker != null) &&
+                (mDataConnectionTracker.isDataPossible(apnType)));
     }
 
     /**
@@ -1008,7 +1062,7 @@ public abstract class PhoneBase extends Handler implements Phone {
                 break;
         }
 
-        mDataConnection.setState(dcState);
+        mDataConnectionTracker.setState(dcState);
         notifyDataConnection(null, Phone.APN_TYPE_DEFAULT);
     }
 
@@ -1080,4 +1134,22 @@ public abstract class PhoneBase extends Handler implements Phone {
         Log.e(LOG_TAG, "Error! " + name + "() in PhoneBase should not be " +
                 "called, GSMPhone inactive.");
     }
+
+    // Called by SimRecords which is constructed with a PhoneBase instead of a GSMPhone.
+    public void notifyCallForwardingIndicator() {
+        // This function should be overridden by the class GSMPhone. Not implemented in CDMAPhone.
+        Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+    }
+
+    public void notifyDataConnectionFailed(String reason, String apnType) {
+        mNotifier.notifyDataConnectionFailed(this, reason, apnType);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getLteOnCdmaMode() {
+        return mCM.getLteOnCdmaMode();
+    }
 }
index 2e391cb..74bae44 100644 (file)
@@ -21,8 +21,10 @@ import android.net.LocalServerSocket;
 import android.os.Looper;
 import android.provider.Settings;
 import android.util.Log;
+import android.os.SystemProperties;
 
 import com.android.internal.telephony.cdma.CDMAPhone;
+import com.android.internal.telephony.cdma.CDMALTEPhone;
 import com.android.internal.telephony.gsm.GSMPhone;
 import com.android.internal.telephony.sip.SipPhone;
 import com.android.internal.telephony.sip.SipPhoneFactory;
@@ -34,6 +36,7 @@ public class PhoneFactory {
     static final String LOG_TAG = "PHONE";
     static final int SOCKET_OPEN_RETRY_MILLIS = 2 * 1000;
     static final int SOCKET_OPEN_MAX_RETRY = 3;
+
     //***** Class Variables
 
     static private Phone sProxyPhone = null;
@@ -44,8 +47,6 @@ public class PhoneFactory {
     static private Looper sLooper;
     static private Context sContext;
 
-    static final int preferredNetworkMode = RILConstants.PREFERRED_NETWORK_MODE;
-
     static final int preferredCdmaSubscription = RILConstants.PREFERRED_CDMA_SUBSCRIPTION;
 
     //***** Class Methods
@@ -96,15 +97,40 @@ public class PhoneFactory {
 
                 sPhoneNotifier = new DefaultPhoneNotifier();
 
-                //Get preferredNetworkMode from Settings.System
+                // Get preferred network mode
+                int preferredNetworkMode = RILConstants.PREFERRED_NETWORK_MODE;
+                if (BaseCommands.getLteOnCdmaModeStatic() == Phone.LTE_ON_CDMA_TRUE) {
+                    preferredNetworkMode = Phone.NT_MODE_GLOBAL;
+                }
                 int networkMode = Settings.Secure.getInt(context.getContentResolver(),
                         Settings.Secure.PREFERRED_NETWORK_MODE, preferredNetworkMode);
                 Log.i(LOG_TAG, "Network Mode set to " + Integer.toString(networkMode));
 
-                //Get preferredNetworkMode from Settings.System
-                int cdmaSubscription = Settings.Secure.getInt(context.getContentResolver(),
-                        Settings.Secure.PREFERRED_CDMA_SUBSCRIPTION, preferredCdmaSubscription);
-                Log.i(LOG_TAG, "Cdma Subscription set to " + Integer.toString(cdmaSubscription));
+                // Get cdmaSubscription
+                // TODO: Change when the ril will provides a way to know at runtime
+                //       the configuration, bug 4202572. And the ril issues the
+                //       RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED, bug 4295439.
+                int cdmaSubscription;
+                int lteOnCdma = BaseCommands.getLteOnCdmaModeStatic();
+                switch (lteOnCdma) {
+                    case Phone.LTE_ON_CDMA_FALSE:
+                        cdmaSubscription = RILConstants.SUBSCRIPTION_FROM_NV;
+                        Log.i(LOG_TAG, "lteOnCdma is 0 use SUBSCRIPTION_FROM_NV");
+                        break;
+                    case Phone.LTE_ON_CDMA_TRUE:
+                        cdmaSubscription = RILConstants.SUBSCRIPTION_FROM_RUIM;
+                        Log.i(LOG_TAG, "lteOnCdma is 1 use SUBSCRIPTION_FROM_RUIM");
+                        break;
+                    case Phone.LTE_ON_CDMA_UNKNOWN:
+                    default:
+                        //Get cdmaSubscription mode from Settings.System
+                        cdmaSubscription = Settings.Secure.getInt(context.getContentResolver(),
+                                Settings.Secure.PREFERRED_CDMA_SUBSCRIPTION,
+                                preferredCdmaSubscription);
+                        Log.i(LOG_TAG, "lteOnCdma not set, using PREFERRED_CDMA_SUBSCRIPTION");
+                        break;
+                }
+                Log.i(LOG_TAG, "Cdma Subscription set to " + cdmaSubscription);
 
                 //reads the system properties and makes commandsinterface
                 sCommandsInterface = new RIL(context, networkMode, cdmaSubscription);
@@ -115,9 +141,19 @@ public class PhoneFactory {
                     sProxyPhone = new PhoneProxy(new GSMPhone(context,
                             sCommandsInterface, sPhoneNotifier));
                 } else if (phoneType == Phone.PHONE_TYPE_CDMA) {
-                    Log.i(LOG_TAG, "Creating CDMAPhone");
-                    sProxyPhone = new PhoneProxy(new CDMAPhone(context,
-                            sCommandsInterface, sPhoneNotifier));
+                    switch (BaseCommands.getLteOnCdmaModeStatic()) {
+                        case Phone.LTE_ON_CDMA_TRUE:
+                            Log.i(LOG_TAG, "Creating CDMALTEPhone");
+                            sProxyPhone = new PhoneProxy(new CDMALTEPhone(context,
+                                sCommandsInterface, sPhoneNotifier));
+                            break;
+                        case Phone.LTE_ON_CDMA_FALSE:
+                        default:
+                            Log.i(LOG_TAG, "Creating CDMAPhone");
+                            sProxyPhone = new PhoneProxy(new CDMAPhone(context,
+                                    sCommandsInterface, sPhoneNotifier));
+                            break;
+                    }
                 }
 
                 sMadeDefaults = true;
@@ -145,8 +181,18 @@ public class PhoneFactory {
         case RILConstants.NETWORK_MODE_GSM_UMTS:
             return Phone.PHONE_TYPE_GSM;
 
+        // Use CDMA Phone for the global mode including CDMA
         case RILConstants.NETWORK_MODE_GLOBAL:
+        case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO:
+        case RILConstants.NETWORK_MODE_LTE_CMDA_EVDO_GSM_WCDMA:
             return Phone.PHONE_TYPE_CDMA;
+
+        case RILConstants.NETWORK_MODE_LTE_ONLY:
+            if (BaseCommands.getLteOnCdmaModeStatic() == Phone.LTE_ON_CDMA_TRUE) {
+                return Phone.PHONE_TYPE_CDMA;
+            } else {
+                return Phone.PHONE_TYPE_GSM;
+            }
         default:
             return Phone.PHONE_TYPE_GSM;
         }
@@ -165,10 +211,22 @@ public class PhoneFactory {
     }
 
     public static Phone getCdmaPhone() {
+        Phone phone;
         synchronized(PhoneProxy.lockForRadioTechnologyChange) {
-            Phone phone = new CDMAPhone(sContext, sCommandsInterface, sPhoneNotifier);
-            return phone;
+            switch (BaseCommands.getLteOnCdmaModeStatic()) {
+                case Phone.LTE_ON_CDMA_TRUE: {
+                    phone = new CDMALTEPhone(sContext, sCommandsInterface, sPhoneNotifier);
+                    break;
+                }
+                case Phone.LTE_ON_CDMA_FALSE:
+                case Phone.LTE_ON_CDMA_UNKNOWN:
+                default: {
+                    phone = new CDMAPhone(sContext, sCommandsInterface, sPhoneNotifier);
+                    break;
+                }
+            }
         }
+        return phone;
     }
 
     public static Phone getGsmPhone() {
index 15b23bb..c2212db 100644 (file)
@@ -208,8 +208,8 @@ public class PhoneProxy extends Handler implements Phone {
         return mActivePhone.getActiveApnTypes();
     }
 
-    public String getActiveApnHost() {
-        return mActivePhone.getActiveApnHost();
+    public String getActiveApnHost(String apnType) {
+        return mActivePhone.getActiveApnHost(apnType);
     }
 
     public LinkProperties getLinkProperties(String apnType) {
@@ -593,10 +593,6 @@ public class PhoneProxy extends Handler implements Phone {
         mActivePhone.getDataCallList(response);
     }
 
-    public List<DataConnection> getCurrentDataConnectionList() {
-        return mActivePhone.getCurrentDataConnectionList();
-    }
-
     public void updateServiceLocation() {
         mActivePhone.updateServiceLocation();
     }
@@ -658,7 +654,11 @@ public class PhoneProxy extends Handler implements Phone {
     }
 
     public boolean isDataConnectivityPossible() {
-        return mActivePhone.isDataConnectivityPossible();
+        return mActivePhone.isDataConnectivityPossible(Phone.APN_TYPE_DEFAULT);
+    }
+
+    public boolean isDataConnectivityPossible(String apnType) {
+        return mActivePhone.isDataConnectivityPossible(apnType);
     }
 
     public String getDeviceId() {
@@ -741,6 +741,10 @@ public class PhoneProxy extends Handler implements Phone {
          return mActivePhone.getCdmaEriIconMode();
     }
 
+    public Phone getActivePhone() {
+         return mActivePhone;
+    }
+
     public void sendBurstDtmf(String dtmfString, int on, int off, Message onComplete){
         mActivePhone.sendBurstDtmf(dtmfString, on, off, onComplete);
     }
@@ -832,4 +836,12 @@ public class PhoneProxy extends Handler implements Phone {
     public boolean isCspPlmnEnabled() {
         return mActivePhone.isCspPlmnEnabled();
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getLteOnCdmaMode() {
+        return mActivePhone.getLteOnCdmaMode();
+    }
 }
index 804ace4..07780ed 100644 (file)
@@ -207,9 +207,8 @@ class RILRequest {
  */
 public final class RIL extends BaseCommands implements CommandsInterface {
     static final String LOG_TAG = "RILJ";
-    private static final boolean DBG = false;
-    static final boolean RILJ_LOGD = Config.LOGD;
-    static final boolean RILJ_LOGV = DBG ? Config.LOGD : Config.LOGV;
+    static final boolean RILJ_LOGD = true;
+    static final boolean RILJ_LOGV = false; // STOP SHIP if true
 
     /**
      * Wake lock timeout should be longer than the longest timeout in
@@ -236,9 +235,6 @@ public final class RIL extends BaseCommands implements CommandsInterface {
     // WAKE_LOCK_TIMEOUT occurs.
     int mRequestMessagesWaiting;
 
-    // Is this the first radio state change?
-    private boolean mInitialRadioStateChange = true;
-
     //I'd rather this be LinkedList or something
     ArrayList<RILRequest> mRequestsList = new ArrayList<RILRequest>();
 
@@ -606,41 +602,25 @@ public final class RIL extends BaseCommands implements CommandsInterface {
             }} catch (Throwable tr) {
                 Log.e(LOG_TAG,"Uncaught exception", tr);
             }
+
+            /* We're disconnected so we don't know the ril version */
+            notifyRegistrantsRilConnectionChanged(-1);
         }
     }
 
 
 
     //***** Constructors
-    public
-    RIL(Context context) {
-        this(context, RILConstants.PREFERRED_NETWORK_MODE,
-                RILConstants.PREFERRED_CDMA_SUBSCRIPTION);
-    }
 
-    public RIL(Context context, int networkMode, int cdmaSubscription) {
+    public RIL(Context context, int preferredNetworkType, int cdmaSubscription) {
         super(context);
-        mCdmaSubscription  = cdmaSubscription;
-        mNetworkMode = networkMode;
-        //At startup mPhoneType is first set from networkMode
-        switch(networkMode) {
-            case RILConstants.NETWORK_MODE_WCDMA_PREF:
-            case RILConstants.NETWORK_MODE_GSM_ONLY:
-            case RILConstants.NETWORK_MODE_WCDMA_ONLY:
-            case RILConstants.NETWORK_MODE_GSM_UMTS:
-                mPhoneType = RILConstants.GSM_PHONE;
-                break;
-            case RILConstants.NETWORK_MODE_CDMA:
-            case RILConstants.NETWORK_MODE_CDMA_NO_EVDO:
-            case RILConstants.NETWORK_MODE_EVDO_NO_CDMA:
-                mPhoneType = RILConstants.CDMA_PHONE;
-                break;
-            case RILConstants.NETWORK_MODE_GLOBAL:
-                mPhoneType = RILConstants.CDMA_PHONE;
-                break;
-            default:
-                mPhoneType = RILConstants.CDMA_PHONE;
+        if (RILJ_LOGD) {
+            riljLog("RIL(context, preferredNetworkType=" + preferredNetworkType +
+                    " cdmaSubscription=" + cdmaSubscription + ")");
         }
+        mCdmaSubscription  = cdmaSubscription;
+        mPreferredNetworkType = preferredNetworkType;
+        mPhoneType = RILConstants.NO_PHONE;
 
         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
@@ -904,10 +884,7 @@ public final class RIL extends BaseCommands implements CommandsInterface {
     getIMSI(Message result) {
         RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_IMSI, result);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() +
-                              "> getIMSI:RIL_REQUEST_GET_IMSI " +
-                              RIL_REQUEST_GET_IMSI +
-                              " " + requestToString(rr.mRequest));
+        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
 
         send(rr);
     }
@@ -1385,37 +1362,15 @@ public final class RIL extends BaseCommands implements CommandsInterface {
 
     public void
     setRadioPower(boolean on, Message result) {
-        //if radio is OFF set preferred NW type and cmda subscription
-        if(mInitialRadioStateChange) {
-            synchronized (mStateMonitor) {
-                if (!mState.isOn()) {
-                    RILRequest rrPnt = RILRequest.obtain(
-                                   RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE, null);
-
-                    rrPnt.mp.writeInt(1);
-                    rrPnt.mp.writeInt(mNetworkMode);
-                    if (RILJ_LOGD) riljLog(rrPnt.serialString() + "> "
-                        + requestToString(rrPnt.mRequest) + " : " + mNetworkMode);
-
-                    send(rrPnt);
-
-                    RILRequest rrCs = RILRequest.obtain(
-                                   RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE, null);
-                    rrCs.mp.writeInt(1);
-                    rrCs.mp.writeInt(mCdmaSubscription);
-                    if (RILJ_LOGD) riljLog(rrCs.serialString() + "> "
-                    + requestToString(rrCs.mRequest) + " : " + mCdmaSubscription);
-                    send(rrCs);
-                }
-            }
-        }
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_RADIO_POWER, result);
+        RILRequest rr = RILRequest.obtain(RIL_REQUEST_RADIO_POWER, result);
 
         rr.mp.writeInt(1);
         rr.mp.writeInt(on ? 1 : 0);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                    + (on ? " on" : " off"));
+        }
 
         send(rr);
     }
@@ -1859,6 +1814,8 @@ public final class RIL extends BaseCommands implements CommandsInterface {
         rr.mp.writeInt(1);
         rr.mp.writeInt(networkType);
 
+        mPreferredNetworkType = networkType;
+
         if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                 + " : " + networkType);
 
@@ -2053,25 +2010,7 @@ public final class RIL extends BaseCommands implements CommandsInterface {
     }
 
     private void switchToRadioState(RadioState newState) {
-
-        if (mInitialRadioStateChange) {
-            if (newState.isOn()) {
-                /* If this is our first notification, make sure the radio
-                 * is powered off.  This gets the radio into a known state,
-                 * since it's possible for the phone proc to have restarted
-                 * (eg, if it or the runtime crashed) without the RIL
-                 * and/or radio knowing.
-                 */
-                if (RILJ_LOGD) Log.d(LOG_TAG, "Radio ON @ init; reset to OFF");
-                setRadioPower(false, null);
-            } else {
-                if (DBG) Log.d(LOG_TAG, "Radio OFF @ init");
-                setRadioState(newState);
-            }
-            mInitialRadioStateChange = false;
-        } else {
-            setRadioState(newState);
-        }
+        setRadioState(newState);
     }
 
     /**
@@ -2286,7 +2225,7 @@ public final class RIL extends BaseCommands implements CommandsInterface {
             case RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM: ret =  responseInts(p); break;
             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_PREFERRED_NETWORK_TYPE: ret =  responseGetPreferredNetworkType(p); break;
             case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: ret = responseCellList(p); break;
             case RIL_REQUEST_SET_LOCATION_UPDATES: ret =  responseVoid(p); break;
             case RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE: ret =  responseVoid(p); break;
@@ -2361,7 +2300,10 @@ public final class RIL extends BaseCommands implements CommandsInterface {
             case RIL_REQUEST_GET_IMSI:
             case RIL_REQUEST_GET_IMEI:
             case RIL_REQUEST_GET_IMEISV:
-                return "";
+                if (!RILJ_LOGV) {
+                    // If not versbose logging just return and don't display IMSI and IMEI, IMEISV
+                    return "";
+                }
         }
 
         StringBuilder sb;
@@ -2458,9 +2400,10 @@ public final class RIL extends BaseCommands implements CommandsInterface {
             case RIL_UNSOL_OEM_HOOK_RAW: ret = responseRaw(p); break;
             case RIL_UNSOL_RINGBACK_TONE: ret = responseInts(p); break;
             case RIL_UNSOL_RESEND_INCALL_MUTE: ret = responseVoid(p); break;
-            case RIL_UNSOL_CDMA_SUBSCRIPTION_CHANGED: ret = responseInts(p); break;
+            case RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED: ret = responseInts(p); break;
             case RIL_UNSOl_CDMA_PRL_CHANGED: ret = responseInts(p); break;
             case RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE: ret = responseVoid(p); break;
+            case RIL_UNSOL_RIL_CONNECTED: ret = responseInts(p); break;
 
             default:
                 throw new RuntimeException("Unrecognized unsol response: " + response);
@@ -2639,8 +2582,8 @@ public final class RIL extends BaseCommands implements CommandsInterface {
             case RIL_UNSOL_SIM_REFRESH:
                 if (RILJ_LOGD) unsljLogRet(response, ret);
 
-                if (mIccRefreshRegistrant != null) {
-                    mIccRefreshRegistrant.notifyRegistrant(
+                if (mIccRefreshRegistrants != null) {
+                    mIccRefreshRegistrants.notifyRegistrants(
                             new AsyncResult (null, ret, null));
                 }
                 break;
@@ -2765,7 +2708,7 @@ public final class RIL extends BaseCommands implements CommandsInterface {
                 }
                 break;
 
-            case RIL_UNSOL_CDMA_SUBSCRIPTION_CHANGED:
+            case RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED:
                 if (RILJ_LOGD) unsljLogRet(response, ret);
 
                 if (mCdmaSubscriptionChangedRegistrants != null) {
@@ -2791,6 +2734,30 @@ public final class RIL extends BaseCommands implements CommandsInterface {
                                         new AsyncResult (null, null, null));
                 }
                 break;
+
+            case RIL_UNSOL_RIL_CONNECTED: {
+                if (RILJ_LOGD) unsljLogRet(response, ret);
+
+                // Initial conditions
+                setRadioPower(false, null);
+                setPreferredNetworkType(mPreferredNetworkType, null);
+                setCdmaSubscriptionSource(mCdmaSubscription, null);
+                notifyRegistrantsRilConnectionChanged(((int[])ret)[0]);
+                break;
+            }
+        }
+    }
+
+    /**
+     * Notifiy all registrants that the ril has connected or disconnected.
+     *
+     * @param rilVer is the version of the ril or -1 if disconnected.
+     */
+    private void notifyRegistrantsRilConnectionChanged(int rilVer) {
+        mRilVersion = rilVer;
+        if (mRilConnectedRegistrants != null) {
+            mRilConnectedRegistrants.notifyRegistrants(
+                                new AsyncResult (null, new Integer(rilVer), null));
         }
     }
 
@@ -2924,7 +2891,7 @@ public final class RIL extends BaseCommands implements CommandsInterface {
 
         String s = p.readString();
 
-        if (RILJ_LOGD) riljLog("< iccIO: "
+        if (RILJ_LOGV) riljLog("< iccIO: "
                 + " 0x" + Integer.toHexString(sw1)
                 + " 0x" + Integer.toHexString(sw2) + " "
                 + s);
@@ -2958,8 +2925,8 @@ public final class RIL extends BaseCommands implements CommandsInterface {
             ca.aid            = p.readString();
             ca.app_label      = p.readString();
             ca.pin1_replaced  = p.readInt();
-            ca.pin1           = p.readInt();
-            ca.pin2           = p.readInt();
+            ca.pin1           = ca.PinStateFromRILInt(p.readInt());
+            ca.pin2           = ca.PinStateFromRILInt(p.readInt());
             status.addApplication(ca);
         }
         return status;
@@ -3038,16 +3005,18 @@ public final class RIL extends BaseCommands implements CommandsInterface {
             dataCall.active = p.readInt();
             dataCall.type = p.readString();
             String addresses = p.readString();
-            if (TextUtils.isEmpty(addresses)) {
+            if (!TextUtils.isEmpty(addresses)) {
                 dataCall.addresses = addresses.split(" ");
             }
         } else {
             dataCall.status = p.readInt();
+            dataCall.suggestedRetryTime = p.readInt();
             dataCall.cid = p.readInt();
             dataCall.active = p.readInt();
             dataCall.type = p.readString();
             dataCall.ifname = p.readString();
-            if (TextUtils.isEmpty(dataCall.ifname)) {
+            if ((dataCall.status == DataConnection.FailCause.NONE.getErrorCode()) &&
+                    TextUtils.isEmpty(dataCall.ifname)) {
               throw new RuntimeException("getDataCallState, no ifname");
             }
             String addresses = p.readString();
@@ -3086,7 +3055,7 @@ public final class RIL extends BaseCommands implements CommandsInterface {
     responseSetupDataCall(Parcel p) {
         int ver = p.readInt();
         int num = p.readInt();
-        if (RILJ_LOGD) riljLog("responseSetupDataCall ver=" + ver + " num=" + num);
+        if (RILJ_LOGV) riljLog("responseSetupDataCall ver=" + ver + " num=" + num);
 
         DataCallState dataCall;
 
@@ -3196,6 +3165,18 @@ public final class RIL extends BaseCommands implements CommandsInterface {
        return response;
     }
 
+    private Object responseGetPreferredNetworkType(Parcel p) {
+       int [] response = (int[]) responseInts(p);
+
+       if (response.length >= 1) {
+           // Since this is the response for getPreferredNetworkType
+           // we'll assume that it should be the value we want the
+           // vendor ril to take if we reestablish a connection to it.
+           mPreferredNetworkType = response[0];
+       }
+       return response;
+    }
+
     private Object responseGmsBroadcastConfig(Parcel p) {
         int num;
         ArrayList<SmsBroadcastConfigInfo> response;
@@ -3525,9 +3506,10 @@ public final class RIL extends BaseCommands implements CommandsInterface {
             case RIL_UNSOL_OEM_HOOK_RAW: return "UNSOL_OEM_HOOK_RAW";
             case RIL_UNSOL_RINGBACK_TONE: return "UNSOL_RINGBACK_TONG";
             case RIL_UNSOL_RESEND_INCALL_MUTE: return "UNSOL_RESEND_INCALL_MUTE";
-            case RIL_UNSOL_CDMA_SUBSCRIPTION_CHANGED: return "CDMA_SUBSCRIPTION_CHANGED";
+            case RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED: return "CDMA_SUBSCRIPTION_SOURCE_CHANGED";
             case RIL_UNSOl_CDMA_PRL_CHANGED: return "UNSOL_CDMA_PRL_CHANGED";
             case RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE: return "UNSOL_EXIT_EMERGENCY_CALLBACK_MODE";
+            case RIL_UNSOL_RIL_CONNECTED: return "UNSOL_RIL_CONNECTED";
             default: return "<unknown reponse>";
         }
     }
@@ -3576,7 +3558,9 @@ public final class RIL extends BaseCommands implements CommandsInterface {
         send(rr);
     }
 
-    public void setPhoneType(int phoneType) { //Set by CDMAPhone and GSMPhone constructor
+    @Override
+    public void setPhoneType(int phoneType) { // Called by CDMAPhone and GSMPhone constructor
+        if (RILJ_LOGD) riljLog("setPhoneType=" + phoneType + " old value=" + mPhoneType);
         mPhoneType = phoneType;
     }
 
@@ -3627,12 +3611,12 @@ public final class RIL extends BaseCommands implements CommandsInterface {
     /**
      * {@inheritDoc}
      */
-    public void getCdmaSubscriptionSource(int cdmaSubscription , Message response) {
+    @Override
+    public void getCdmaSubscriptionSource(Message response) {
         RILRequest rr = RILRequest.obtain(
                 RILConstants.RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE, response);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                + " : " + cdmaSubscription);
+        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
 
         send(rr);
     }
index cdf1977..93fc9ce 100644 (file)
@@ -66,6 +66,11 @@ public interface RILConstants {
     int NETWORK_MODE_EVDO_NO_CDMA   = 6; /* EvDo only */
     int NETWORK_MODE_GLOBAL         = 7; /* GSM/WCDMA, CDMA, and EvDo (auto mode, according to PRL)
                                             AVAILABLE Application Settings menu*/
+    int NETWORK_MODE_LTE_CDMA_EVDO  = 8; /* LTE, CDMA and EvDo */
+    int NETWORK_MODE_LTE_GSM_WCDMA  = 9; /* LTE, GSM/WCDMA */
+    int NETWORK_MODE_LTE_CMDA_EVDO_GSM_WCDMA = 10; /* LTE, CDMA, EvDo, GSM/WCDMA */
+    int NETWORK_MODE_LTE_ONLY       = 11; /* LTE Only mode. */
+
     int PREFERRED_NETWORK_MODE      = NETWORK_MODE_WCDMA_PREF;
 
     /* CDMA subscription source. See ril.h RIL_REQUEST_CDMA_SET_SUBSCRIPTION */
@@ -81,6 +86,10 @@ public interface RILConstants {
     int CDMA_PHONE = 2;
     int SIP_PHONE  = 3;
 
+    int LTE_ON_CDMA_UNKNOWN = -1;
+    int LTE_ON_CDMA_FALSE = 0;
+    int LTE_ON_CDMA_TRUE = 1;
+
     int CDM_TTY_MODE_DISABLED = 0;
     int CDM_TTY_MODE_ENABLED = 1;
 
@@ -104,6 +113,7 @@ public interface RILConstants {
     /* Deactivate data call reasons */
     int DEACTIVATE_REASON_NONE = 0;
     int DEACTIVATE_REASON_RADIO_OFF = 1;
+    int DEACTIVATE_REASON_PDP_RESET = 2;
 
 /*
 cat include/telephony/ril.h | \
@@ -138,10 +148,12 @@ cat include/telephony/ril.h | \
     int RIL_RESTRICTED_STATE_PS_ALL = 0x10;
 
     /** Data profile for RIL_REQUEST_SETUP_DATA_CALL */
-    static final int DATA_PROFILE_DEFAULT   = 0;
-    static final int DATA_PROFILE_TETHERED  = 1;
-    static final int DATA_PROFILE_OEM_BASE  = 1000;
-
+    public static final int DATA_PROFILE_DEFAULT   = 0;
+    public static final int DATA_PROFILE_TETHERED  = 1;
+    public static final int DATA_PROFILE_IMS       = 2;
+    public static final int DATA_PROFILE_FOTA      = 3;
+    public static final int DATA_PROFILE_CBS       = 4;
+    public static final int DATA_PROFILE_OEM_BASE  = 1000;
 
     int RIL_REQUEST_GET_SIM_STATUS = 1;
     int RIL_REQUEST_ENTER_SIM_PIN = 2;
@@ -279,7 +291,8 @@ cat include/telephony/ril.h | \
     int RIL_UNSOL_OEM_HOOK_RAW = 1028;
     int RIL_UNSOL_RINGBACK_TONE = 1029;
     int RIL_UNSOL_RESEND_INCALL_MUTE = 1030;
-    int RIL_UNSOL_CDMA_SUBSCRIPTION_CHANGED = 1031;
+    int RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED = 1031;
     int RIL_UNSOl_CDMA_PRL_CHANGED = 1032;
     int RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE = 1033;
+    int RIL_UNSOL_RIL_CONNECTED = 1034;
 }
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.internal.telephony.gsm;
+package com.android.internal.telephony;
 
 import android.telephony.ServiceState;
 
index b1049a2..29bd104 100644 (file)
@@ -308,12 +308,10 @@ public class RetryManager {
     }
 
     /**
-     * Reset network re-registration indicator and clear the data-retry counter
-     * and turns off retrying forever.
+     * Clear the data-retry counter
      */
     public void resetRetryCount() {
         mRetryCount = 0;
-        mRetryForever = false;
         if (DBG) log("resetRetryCount: " + mRetryCount);
     }
 
index befee8c..6af9b1c 100755 (executable)
@@ -582,7 +582,7 @@ public abstract class SMSDispatcher extends Handler {
      *         {@link Activity#RESULT_OK} if the message has been broadcast
      *         to applications
      */
-    protected abstract int dispatchMessage(SmsMessageBase sms);
+    public abstract int dispatchMessage(SmsMessageBase sms);
 
 
     /**
@@ -916,7 +916,7 @@ public abstract class SMSDispatcher extends Handler {
      * @param response
      *            Callback message is empty on completion
      */
-    protected abstract void activateCellBroadcastSms(int activate, Message response);
+    public abstract void activateCellBroadcastSms(int activate, Message response);
 
     /**
      * Query the current configuration of cell broadcast SMS.
@@ -925,7 +925,7 @@ public abstract class SMSDispatcher extends Handler {
      *            Callback message contains the configuration from the modem on completion
      *            @see #setCellBroadcastConfig
      */
-    protected abstract void getCellBroadcastSmsConfig(Message response);
+    public abstract void getCellBroadcastSmsConfig(Message response);
 
     /**
      * Configure cell broadcast SMS.
@@ -937,7 +937,7 @@ public abstract class SMSDispatcher extends Handler {
      * @param response
      *            Callback message is empty on completion
      */
-    protected abstract void setCellBroadcastConfig(int[] configValuesArray, Message response);
+    public abstract void setCellBroadcastConfig(int[] configValuesArray, Message response);
 
     /**
      * Send an acknowledge message.
@@ -1006,6 +1006,27 @@ public abstract class SMSDispatcher extends Handler {
         return new SmsTracker(data, sentIntent, deliveryIntent);
     }
 
+    public void initSipStack(boolean isObg) {
+        // This function should be overridden by the classes that support
+        // switching modes such as the CdmaSMSDispatcher.
+        // Not implemented in GsmSMSDispatcher.
+        Log.e(TAG, "Error! This function should never be executed.");
+    }
+
+    public void switchToCdma() {
+        // This function should be overridden by the classes that support
+        // switching modes such as the CdmaSMSDispatcher.
+        // Not implemented in GsmSMSDispatcher.
+        Log.e(TAG, "Error! This function should never be executed.");
+    }
+
+    public void switchToGsm() {
+        // This function should be overridden by the classes that support
+        // switching modes such as the CdmaSMSDispatcher.
+        // Not implemented in GsmSMSDispatcher.
+        Log.e(TAG, "Error! This function should never be executed.");
+    }
+
     private DialogInterface.OnClickListener mListener =
         new DialogInterface.OnClickListener() {
 
index 3f9ffc3..01b807d 100644 (file)
@@ -29,23 +29,6 @@ import android.telephony.SignalStrength;
  */
 public abstract class ServiceStateTracker extends Handler {
 
-    /**
-     *  Access technology currently in use.
-     */
-    protected static final int DATA_ACCESS_UNKNOWN = 0;
-    protected static final int DATA_ACCESS_GPRS = 1;
-    protected static final int DATA_ACCESS_EDGE = 2;
-    protected static final int DATA_ACCESS_UMTS = 3;
-    protected static final int DATA_ACCESS_CDMA_IS95A = 4;
-    protected static final int DATA_ACCESS_CDMA_IS95B = 5;
-    protected static final int DATA_ACCESS_CDMA_1xRTT = 6;
-    protected static final int DATA_ACCESS_CDMA_EvDo_0 = 7;
-    protected static final int DATA_ACCESS_CDMA_EvDo_A = 8;
-    protected static final int DATA_ACCESS_HSDPA = 9;
-    protected static final int DATA_ACCESS_HSUPA = 10;
-    protected static final int DATA_ACCESS_HSPA = 11;
-    protected static final int DATA_ACCESS_CDMA_EvDo_B = 12;
-
     protected CommandsInterface cm;
 
     public ServiceState ss;
@@ -53,6 +36,9 @@ public abstract class ServiceStateTracker extends Handler {
 
     public SignalStrength mSignalStrength;
 
+    // TODO - this should not be public
+    public RestrictedState mRestrictedState = new RestrictedState();
+
     /* The otaspMode passed to PhoneStateListener#onOtaspChanged */
     static public final int OTASP_UNINITIALIZED = 0;
     static public final int OTASP_UNKNOWN = 1;
@@ -68,15 +54,29 @@ public abstract class ServiceStateTracker extends Handler {
     protected boolean mDesiredPowerState;
 
     /**
+     *  Values correspond to ServiceState.RADIO_TECHNOLOGY_ definitions.
+     */
+    protected int mRadioTechnology = 0;
+    protected int mNewRadioTechnology = 0;
+
+    /**
      * By default, strength polling is enabled.  However, if we're
      * getting unsolicited signal strength updates from the radio, set
      * value to true and don't bother polling any more.
      */
     protected boolean dontPollSignalStrength = false;
 
-    protected RegistrantList networkAttachedRegistrants = new RegistrantList();
-    protected RegistrantList roamingOnRegistrants = new RegistrantList();
-    protected RegistrantList roamingOffRegistrants = new RegistrantList();
+    protected RegistrantList mRoamingOnRegistrants = new RegistrantList();
+    protected RegistrantList mRoamingOffRegistrants = new RegistrantList();
+    protected RegistrantList mAttachedRegistrants = new RegistrantList();
+    protected RegistrantList mDetachedRegistrants = new RegistrantList();
+    protected RegistrantList mNetworkAttachedRegistrants = new RegistrantList();
+    protected RegistrantList mPsRestrictEnabledRegistrants = new RegistrantList();
+    protected RegistrantList mPsRestrictDisabledRegistrants = new RegistrantList();
+
+    /* Radio power off pending flag and tag counter */
+    private boolean mPendingRadioPowerOffAfterDataOff = false;
+    private int mPendingRadioPowerOffAfterDataOffTag = 0;
 
     protected  static final boolean DBG = true;
 
@@ -86,8 +86,6 @@ public abstract class ServiceStateTracker extends Handler {
     /** Waiting period before recheck gprs and voice registration. */
     public static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000;
 
-    public static final int DATA_STATE_POLL_SLEEP_MS = 100;
-
     /** GSM events */
     protected static final int EVENT_RADIO_STATE_CHANGED               = 1;
     protected static final int EVENT_NETWORK_STATE_CHANGED             = 2;
@@ -163,7 +161,6 @@ public abstract class ServiceStateTracker extends Handler {
     protected static final String REGISTRATION_DENIED_AUTH = "Authentication Failure";
 
     public ServiceStateTracker() {
-
     }
 
     public boolean getDesiredPowerState() {
@@ -180,7 +177,7 @@ public abstract class ServiceStateTracker extends Handler {
      */
     public  void registerForRoamingOn(Handler h, int what, Object obj) {
         Registrant r = new Registrant(h, what, obj);
-        roamingOnRegistrants.add(r);
+        mRoamingOnRegistrants.add(r);
 
         if (ss.getRoaming()) {
             r.notifyRegistrant();
@@ -188,7 +185,7 @@ public abstract class ServiceStateTracker extends Handler {
     }
 
     public  void unregisterForRoamingOn(Handler h) {
-        roamingOnRegistrants.remove(h);
+        mRoamingOnRegistrants.remove(h);
     }
 
     /**
@@ -201,7 +198,7 @@ public abstract class ServiceStateTracker extends Handler {
      */
     public  void registerForRoamingOff(Handler h, int what, Object obj) {
         Registrant r = new Registrant(h, what, obj);
-        roamingOffRegistrants.add(r);
+        mRoamingOffRegistrants.add(r);
 
         if (!ss.getRoaming()) {
             r.notifyRegistrant();
@@ -209,7 +206,7 @@ public abstract class ServiceStateTracker extends Handler {
     }
 
     public  void unregisterForRoamingOff(Handler h) {
-        roamingOffRegistrants.remove(h);
+        mRoamingOffRegistrants.remove(h);
     }
 
     /**
@@ -272,20 +269,183 @@ public abstract class ServiceStateTracker extends Handler {
         }
     }
 
-    public abstract void handleMessage(Message msg);
+    @Override
+    public void handleMessage(Message msg) {
+        switch (msg.what) {
+            case EVENT_SET_RADIO_POWER_OFF:
+                synchronized(this) {
+                    if (mPendingRadioPowerOffAfterDataOff &&
+                            (msg.arg1 == mPendingRadioPowerOffAfterDataOffTag)) {
+                        if (DBG) log("EVENT_SET_RADIO_OFF, turn radio off now.");
+                        hangupAndPowerOff();
+                        mPendingRadioPowerOffAfterDataOffTag += 1;
+                        mPendingRadioPowerOffAfterDataOff = false;
+                    } else {
+                        log("EVENT_SET_RADIO_OFF is stale arg1=" + msg.arg1 +
+                                "!= tag=" + mPendingRadioPowerOffAfterDataOffTag);
+                    }
+                }
+                break;
+
+            default:
+                log("Unhandled message with number: " + msg.what);
+                break;
+        }
+    }
 
     protected abstract Phone getPhone();
     protected abstract void handlePollStateResult(int what, AsyncResult ar);
     protected abstract void updateSpnDisplay();
     protected abstract void setPowerStateToDesired();
     protected abstract void log(String s);
+    protected abstract void loge(String s);
+
+    public abstract int getCurrentDataConnectionState();
+    public abstract boolean isConcurrentVoiceAndDataAllowed();
+
+    /**
+     * Registration point for transition into DataConnection attached.
+     * @param h handler to notify
+     * @param what what code of message when delivered
+     * @param obj placed in Message.obj
+     */
+    public void registerForDataConnectionAttached(Handler h, int what, Object obj) {
+        Registrant r = new Registrant(h, what, obj);
+        mAttachedRegistrants.add(r);
+
+        if (getCurrentDataConnectionState() == ServiceState.STATE_IN_SERVICE) {
+            r.notifyRegistrant();
+        }
+    }
+    public void unregisterForDataConnectionAttached(Handler h) {
+        mAttachedRegistrants.remove(h);
+    }
+
+    /**
+     * Registration point for transition into DataConnection detached.
+     * @param h handler to notify
+     * @param what what code of message when delivered
+     * @param obj placed in Message.obj
+     */
+    public void registerForDataConnectionDetached(Handler h, int what, Object obj) {
+        Registrant r = new Registrant(h, what, obj);
+        mDetachedRegistrants.add(r);
+
+        if (getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE) {
+            r.notifyRegistrant();
+        }
+    }
+    public void unregisterForDataConnectionDetached(Handler h) {
+        mDetachedRegistrants.remove(h);
+    }
+
+    /**
+     * Registration point for transition into network attached.
+     * @param h handler to notify
+     * @param what what code of message when delivered
+     * @param obj in Message.obj
+     */
+    public void registerForNetworkAttached(Handler h, int what, Object obj) {
+        Registrant r = new Registrant(h, what, obj);
+
+        mNetworkAttachedRegistrants.add(r);
+        if (ss.getState() == ServiceState.STATE_IN_SERVICE) {
+            r.notifyRegistrant();
+        }
+    }
+    public void unregisterForNetworkAttached(Handler h) {
+        mNetworkAttachedRegistrants.remove(h);
+    }
+
+    /**
+     * Registration point for transition into packet service restricted zone.
+     * @param h handler to notify
+     * @param what what code of message when delivered
+     * @param obj placed in Message.obj
+     */
+    public void registerForPsRestrictedEnabled(Handler h, int what, Object obj) {
+        Registrant r = new Registrant(h, what, obj);
+        mPsRestrictEnabledRegistrants.add(r);
+
+        if (mRestrictedState.isPsRestricted()) {
+            r.notifyRegistrant();
+        }
+    }
+
+    public void unregisterForPsRestrictedEnabled(Handler h) {
+        mPsRestrictEnabledRegistrants.remove(h);
+    }
+
+    /**
+     * Registration point for transition out of packet service restricted zone.
+     * @param h handler to notify
+     * @param what what code of message when delivered
+     * @param obj placed in Message.obj
+     */
+    public void registerForPsRestrictedDisabled(Handler h, int what, Object obj) {
+        Registrant r = new Registrant(h, what, obj);
+        mPsRestrictDisabledRegistrants.add(r);
+
+        if (mRestrictedState.isPsRestricted()) {
+            r.notifyRegistrant();
+        }
+    }
+
+    public void unregisterForPsRestrictedDisabled(Handler h) {
+        mPsRestrictDisabledRegistrants.remove(h);
+    }
 
     /**
      * Clean up existing voice and data connection then turn off radio power.
      *
      * Hang up the existing voice calls to decrease call drop rate.
      */
-    protected abstract void powerOffRadioSafely();
+    public void powerOffRadioSafely(DataConnectionTracker dcTracker) {
+        synchronized (this) {
+            if (!mPendingRadioPowerOffAfterDataOff) {
+                if (dcTracker.isAnyActiveDataConnections()) {
+                    dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
+                    Message msg = Message.obtain(this);
+                    msg.what = EVENT_SET_RADIO_POWER_OFF;
+                    msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag;
+                    if (sendMessageDelayed(msg, 30000)) {
+                        if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio.");
+                        mPendingRadioPowerOffAfterDataOff = true;
+                    } else {
+                        log("Cannot send delayed Msg, turn off radio right away.");
+                        hangupAndPowerOff();
+                    }
+                } else {
+                    dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
+                    if (DBG) log("Data disconnected, turn off radio right away.");
+                    hangupAndPowerOff();
+                }
+            }
+        }
+    }
+
+    /**
+     * process the pending request to turn radio off after data is disconnected
+     *
+     * return true if there is pending request to process; false otherwise.
+     */
+    public boolean processPendingRadioPowerOffAfterDataOff() {
+        synchronized(this) {
+            if (mPendingRadioPowerOffAfterDataOff) {
+                if (DBG) log("Process pending request to turn radio off.");
+                mPendingRadioPowerOffAfterDataOffTag += 1;
+                hangupAndPowerOff();
+                mPendingRadioPowerOffAfterDataOff = false;
+                return true;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Hang up all voice call and turn off radio. Implemented by derived class.
+     */
+    protected abstract void hangupAndPowerOff();
 
     /** Cancel a pending (if any) pollState() operation */
     protected void cancelPollState() {
index e6189be..4309309 100644 (file)
@@ -72,6 +72,13 @@ public interface TelephonyProperties
      */
     static final String PROPERTY_OPERATOR_ISO_COUNTRY = "gsm.operator.iso-country";
 
+    /**
+     * The contents of this property is the value of the kernel command line
+     * product_type variable that corresponds to a product that supports LTE on CDMA.
+     * {@see BaseCommands#getLteOnCdmaMode()}
+     */
+    static final String PROPERTY_LTE_ON_CDMA_PRODUCT_TYPE = "telephony.lteOnCdmaProductType";
+
     static final String CURRENT_ACTIVE_PHONE = "gsm.current.phone-type";
 
     //****** SIM Card
index 1e23e34..df47350 100644 (file)
@@ -33,6 +33,7 @@ import com.android.internal.telephony.IccRecords;
 import android.util.Config;
 
 import java.io.ByteArrayOutputStream;
+import java.util.Locale;
 
 /**
  * Enumeration for representing the tag value of COMPREHENSION-TLV objects. If
@@ -118,6 +119,8 @@ public class CatService extends Handler implements AppInterface {
     private static IccRecords mIccRecords;
 
     // Service members.
+    // Protects singleton instance lazy initialization.
+    private static final Object sInstanceLock = new Object();
     private static CatService sInstance;
     private CommandsInterface mCmdIf;
     private Context mContext;
@@ -133,6 +136,7 @@ public class CatService extends Handler implements AppInterface {
     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_SIM_READY                = 7;
 
     static final int MSG_ID_RIL_MSG_DECODED          = 10;
 
@@ -170,9 +174,11 @@ public class CatService extends Handler implements AppInterface {
         mIccRecords = ir;
 
         // Register for SIM ready event.
+        mCmdIf.registerForSIMReady(this, MSG_ID_SIM_READY, null);
+        mCmdIf.registerForRUIMReady(this, MSG_ID_SIM_READY, null);
+        mCmdIf.registerForNVReady(this, MSG_ID_SIM_READY, null);
         mIccRecords.registerForRecordsLoaded(this, MSG_ID_ICC_RECORDS_LOADED, null);
 
-        mCmdIf.reportStkServiceIsRunning(null);
         CatLog.d(this, "Is running");
     }
 
@@ -269,8 +275,20 @@ public class CatService extends Handler implements AppInterface {
                 sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
                 break;
             case PROVIDE_LOCAL_INFORMATION:
-                sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
-                return;
+                ResponseData resp;
+                switch (cmdParams.cmdDet.commandQualifier) {
+                    case CommandParamsFactory.DTTZ_SETTING:
+                        resp = new DTTZResponseData(null);
+                        sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, resp);
+                        break;
+                    case CommandParamsFactory.LANGUAGE_SETTING:
+                        resp = new LanguageResponseData(Locale.getDefault().getLanguage());
+                        sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, resp);
+                        break;
+                    default:
+                        sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
+                        return;
+                }
             case LAUNCH_BROWSER:
             case SELECT_ITEM:
             case GET_INPUT:
@@ -373,25 +391,30 @@ public class CatService extends Handler implements AppInterface {
 
     private void encodeOptionalTags(CommandDetails cmdDet,
             ResultCode resultCode, Input cmdInput, ByteArrayOutputStream buf) {
-        switch (AppInterface.CommandType.fromInt(cmdDet.typeOfCommand)) {
-            case GET_INKEY:
-                // ETSI TS 102 384,27.22.4.2.8.4.2.
-                // If it is a response for GET_INKEY command and the response timeout
-                // occured, then add DURATION TLV for variable timeout case.
-                if ((resultCode.value() == ResultCode.NO_RESPONSE_FROM_USER.value()) &&
-                    (cmdInput != null) && (cmdInput.duration != null)) {
-                    getInKeyResponse(buf, cmdInput);
-                }
-                break;
-            case PROVIDE_LOCAL_INFORMATION:
-                if ((cmdDet.commandQualifier == CommandParamsFactory.LANGUAGE_SETTING) &&
-                    (resultCode.value() == ResultCode.OK.value())) {
-                    getPliResponse(buf);
-                }
-                break;
-            default:
-                CatLog.d(this, "encodeOptionalTags() Unsupported Cmd:" + cmdDet.typeOfCommand);
-                break;
+        CommandType cmdType = AppInterface.CommandType.fromInt(cmdDet.typeOfCommand);
+        if (cmdType != null) {
+            switch (cmdType) {
+                case GET_INKEY:
+                    // ETSI TS 102 384,27.22.4.2.8.4.2.
+                    // If it is a response for GET_INKEY command and the response timeout
+                    // occured, then add DURATION TLV for variable timeout case.
+                    if ((resultCode.value() == ResultCode.NO_RESPONSE_FROM_USER.value()) &&
+                        (cmdInput != null) && (cmdInput.duration != null)) {
+                        getInKeyResponse(buf, cmdInput);
+                    }
+                    break;
+                case PROVIDE_LOCAL_INFORMATION:
+                    if ((cmdDet.commandQualifier == CommandParamsFactory.LANGUAGE_SETTING) &&
+                        (resultCode.value() == ResultCode.OK.value())) {
+                        getPliResponse(buf);
+                    }
+                    break;
+                default:
+                    CatLog.d(this, "encodeOptionalTags() Unsupported Cmd:" + cmdDet.typeOfCommand);
+                    break;
+            }
+        } else {
+            CatLog.d(this, "encodeOptionalTags() bad Cmd:" + cmdDet.typeOfCommand);
         }
     }
 
@@ -515,26 +538,28 @@ public class CatService extends Handler implements AppInterface {
      */
     public static CatService getInstance(CommandsInterface ci, IccRecords ir,
             Context context, IccFileHandler fh, IccCard ic) {
-        if (sInstance == null) {
-            if (ci == null || ir == null || context == null || fh == null
-                    || ic == null) {
-                return null;
+        synchronized (sInstanceLock) {
+            if (sInstance == null) {
+                if (ci == null || ir == null || context == null || fh == null
+                        || ic == null) {
+                    return null;
+                }
+                HandlerThread thread = new HandlerThread("Cat Telephony service");
+                thread.start();
+                sInstance = new CatService(ci, ir, context, fh, ic);
+                CatLog.d(sInstance, "NEW sInstance");
+            } else if ((ir != null) && (mIccRecords != ir)) {
+                CatLog.d(sInstance, "Reinitialize the Service with SIMRecords");
+                mIccRecords = ir;
+
+                // re-Register for SIM ready event.
+                mIccRecords.registerForRecordsLoaded(sInstance, MSG_ID_ICC_RECORDS_LOADED, null);
+                CatLog.d(sInstance, "sr changed reinitialize and return current sInstance");
+            } else {
+                CatLog.d(sInstance, "Return current sInstance");
             }
-            HandlerThread thread = new HandlerThread("Cat Telephony service");
-            thread.start();
-            sInstance = new CatService(ci, ir, context, fh, ic);
-            CatLog.d(sInstance, "NEW sInstance");
-        } else if ((ir != null) && (mIccRecords != ir)) {
-            CatLog.d(sInstance, "Reinitialize the Service with SIMRecords");
-            mIccRecords = ir;
-
-            // re-Register for SIM ready event.
-            mIccRecords.registerForRecordsLoaded(sInstance, MSG_ID_ICC_RECORDS_LOADED, null);
-            CatLog.d(sInstance, "sr changed reinitialize and return current sInstance");
-        } else {
-            CatLog.d(sInstance, "Return current sInstance");
+            return sInstance;
         }
-        return sInstance;
     }
 
     /**
@@ -579,6 +604,10 @@ public class CatService extends Handler implements AppInterface {
         case MSG_ID_RESPONSE:
             handleCmdResponse((CatResponseMessage) msg.obj);
             break;
+        case MSG_ID_SIM_READY:
+            CatLog.d(this, "SIM ready. Reporting STK service running now...");
+            mCmdIf.reportStkServiceIsRunning(null);
+            break;
         default:
             throw new AssertionError("Unrecognized CAT command: " + msg.what);
         }
index 12204a0..686fe46 100644 (file)
@@ -53,6 +53,7 @@ class CommandParamsFactory extends Handler {
     static final int REFRESH_UICC_RESET                     = 0x04;
 
     // Command Qualifier values for PLI command
+    static final int DTTZ_SETTING                           = 0x03;
     static final int LANGUAGE_SETTING                       = 0x04;
 
     static synchronized CommandParamsFactory getInstance(RilMessageDecoder caller,
@@ -883,6 +884,10 @@ class CommandParamsFactory extends Handler {
             throws ResultException {
         CatLog.d(this, "process ProvideLocalInfo");
         switch (cmdDet.commandQualifier) {
+            case DTTZ_SETTING:
+                CatLog.d(this, "PLI [DTTZ_SETTING]");
+                mCmdParams = new CommandParams(cmdDet);
+                break;
             case LANGUAGE_SETTING:
                 CatLog.d(this, "PLI [LANGUAGE_SETTING]");
                 mCmdParams = new CommandParams(cmdDet);
index 677d66b..4846a3e 100644 (file)
@@ -18,6 +18,8 @@ package com.android.internal.telephony.cat;
 
 import com.android.internal.telephony.EncodeException;
 import com.android.internal.telephony.GsmAlphabet;
+import java.util.Calendar;
+import com.android.internal.telephony.cat.AppInterface.CommandType;
 
 import java.io.ByteArrayOutputStream;
 import java.io.UnsupportedEncodingException;
@@ -147,4 +149,109 @@ class GetInkeyInputResponseData extends ResponseData {
     }
 }
 
+// For "PROVIDE LOCAL INFORMATION" command.
+// See TS 31.111 section 6.4.15/ETSI TS 102 223
+// TS 31.124 section 27.22.4.15 for test spec
+class LanguageResponseData extends ResponseData {
+    private String lang;
+
+    public LanguageResponseData(String lang) {
+        super();
+        this.lang = lang;
+    }
+
+    @Override
+    public void format(ByteArrayOutputStream buf) {
+        if (buf == null) {
+            return;
+        }
+
+        // Text string object
+        int tag = 0x80 | ComprehensionTlvTag.LANGUAGE.value();
+        buf.write(tag); // tag
+
+        byte[] data;
+
+        if (lang != null && lang.length() > 0) {
+            data = GsmAlphabet.stringToGsm8BitPacked(lang);
+        }
+        else {
+            data = new byte[0];
+        }
+
+        buf.write(data.length);
+
+        for (byte b : data) {
+            buf.write(b);
+        }
+    }
+}
+
+// For "PROVIDE LOCAL INFORMATION" command.
+// See TS 31.111 section 6.4.15/ETSI TS 102 223
+// TS 31.124 section 27.22.4.15 for test spec
+class DTTZResponseData extends ResponseData {
+    private Calendar calendar;
+
+    public DTTZResponseData(Calendar cal) {
+        super();
+        calendar = cal;
+    }
+
+    @Override
+    public void format(ByteArrayOutputStream buf) {
+        if (buf == null) {
+            return;
+        }
+
+        // DTTZ object
+        int tag = 0x80 | CommandType.PROVIDE_LOCAL_INFORMATION.value();
+        buf.write(tag); // tag
+
+        byte[] data = new byte[8];
+        byte btmp; // temp variable
+
+        data[0] = 0x07; // Write length of DTTZ data
+
+        if (calendar == null) {
+            calendar = Calendar.getInstance();
+        }
+        // Fill year byte
+        btmp = (byte) (calendar.get(java.util.Calendar.YEAR) % 100);
+        data[1] = (byte) (btmp / 10);
+        data[1] += (byte) ((btmp % 10) << 4);
+
+        // Fill month byte
+        btmp = (byte) (calendar.get(java.util.Calendar.MONTH) + 1);
+        data[2] = (byte) (btmp / 10);
+        data[2] += (byte) ((btmp % 10) << 4);
+
+        // Fill day byte
+        btmp = (byte) (calendar.get(java.util.Calendar.DATE));
+        data[3] = (byte) (btmp / 10);
+        data[3] += (byte) ((btmp % 10) << 4);
+
+        // Fill hour byte
+        btmp = (byte) (calendar.get(java.util.Calendar.HOUR_OF_DAY));
+        data[4] = (byte) (btmp / 10);
+        data[4] += (byte) ((btmp % 10) << 4);
+
+        // Fill minute byte
+        btmp = (byte) (calendar.get(java.util.Calendar.MINUTE));
+        data[5] = (byte) (btmp / 10);
+        data[5] += (byte) ((btmp % 10) << 4);
+
+        // Fill second byte
+        btmp = (byte) (calendar.get(java.util.Calendar.SECOND));
+        data[6] = (byte) (btmp / 10);
+        data[6] += (byte) ((btmp % 10) << 4);
+
+        // No time zone info
+        data[7] = (byte) 0xFF;
+
+        for (byte b : data) {
+            buf.write(b);
+        }
+    }
+}
 
index a197c9a..2a1f508 100644 (file)
@@ -20,15 +20,15 @@ import com.android.internal.telephony.IccFileHandler;
 import com.android.internal.telephony.IccUtils;
 
 import android.os.Handler;
-import com.android.internal.util.HierarchicalState;
-import com.android.internal.util.HierarchicalStateMachine;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
 import android.os.Message;
 
 /**
  * Class used for queuing raw ril messages, decoding them into CommanParams
  * objects and sending the result back to the CAT Service.
  */
-class RilMessageDecoder extends HierarchicalStateMachine {
+class RilMessageDecoder extends StateMachine {
 
     // constants
     private static final int CMD_START = 1;
@@ -101,8 +101,9 @@ class RilMessageDecoder extends HierarchicalStateMachine {
         mCmdParamsFactory = CommandParamsFactory.getInstance(this, fh);
     }
 
-    private class StateStart extends HierarchicalState {
-        @Override protected boolean processMessage(Message msg) {
+    private class StateStart extends State {
+        @Override
+        public boolean processMessage(Message msg) {
             if (msg.what == CMD_START) {
                 if (decodeMessageParams((RilMessage)msg.obj)) {
                     transitionTo(mStateCmdParamsReady);
@@ -115,8 +116,9 @@ class RilMessageDecoder extends HierarchicalStateMachine {
         }
     }
 
-    private class StateCmdParamsReady extends HierarchicalState {
-        @Override protected boolean processMessage(Message msg) {
+    private class StateCmdParamsReady extends State {
+        @Override
+        public boolean processMessage(Message msg) {
             if (msg.what == CMD_PARAMS_READY) {
                 mCurrentRilMessage.mResCode = ResultCode.fromInt(msg.arg1);
                 mCurrentRilMessage.mData = msg.obj;
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
new file mode 100644 (file)
index 0000000..a31b704
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * 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.cdma;
+
+import android.os.SystemProperties;
+import android.content.Context;
+import android.net.Uri;
+import android.content.Context;
+import android.provider.Telephony;
+import android.content.ContentValues;
+import android.database.SQLException;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+
+import com.android.internal.telephony.gsm.SIMRecords;
+import com.android.internal.telephony.gsm.SimCard;
+import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.DataConnectionTracker;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneBase;
+import com.android.internal.telephony.PhoneNotifier;
+import com.android.internal.telephony.PhoneProxy;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.gsm.GsmDataConnectionTracker;
+
+import android.util.Log;
+
+public class CDMALTEPhone extends CDMAPhone {
+    static final String LOG_TAG = "CDMA";
+
+    private static final boolean DBG = true;
+
+    // Constructors
+    public CDMALTEPhone(Context context, CommandsInterface ci, PhoneNotifier notifier) {
+        this(context, ci, notifier, false);
+    }
+
+    public CDMALTEPhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
+            boolean unitTestMode) {
+        super(context, ci, notifier, false);
+    }
+
+    @Override
+    protected void initSstIcc() {
+        mSST = new CdmaLteServiceStateTracker(this);
+        mIccRecords = new CdmaLteUiccRecords(this);
+        mIccCard = new SimCard(this, LOG_TAG, DBG);
+        mIccFileHandler = new CdmaLteUiccFileHandler(this);
+    }
+
+    @Override
+    public DataState getDataConnectionState(String apnType) {
+        DataState ret = DataState.DISCONNECTED;
+
+        if (mSST == null) {
+            // Radio Technology Change is ongoning, dispose() and
+            // removeReferences() have already been called
+
+            ret = DataState.DISCONNECTED;
+        } else if (mDataConnectionTracker.isApnTypeEnabled(apnType) == false) {
+            ret = DataState.DISCONNECTED;
+        } else {
+            switch (mDataConnectionTracker.getState(apnType)) {
+                case FAILED:
+                case IDLE:
+                    ret = DataState.DISCONNECTED;
+                    break;
+
+                case CONNECTED:
+                case DISCONNECTING:
+                    if (mCT.state != Phone.State.IDLE && !mSST.isConcurrentVoiceAndDataAllowed()) {
+                        ret = DataState.SUSPENDED;
+                    } else {
+                        ret = DataState.CONNECTED;
+                    }
+                    break;
+
+                case INITING:
+                case CONNECTING:
+                case SCANNING:
+                    ret = DataState.CONNECTING;
+                    break;
+            }
+        }
+
+        log("getDataConnectionState apnType=" + apnType + " ret=" + ret);
+        return ret;
+    }
+
+    public boolean updateCurrentCarrierInProvider() {
+        if (mIccRecords != null) {
+            try {
+                Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current");
+                ContentValues map = new ContentValues();
+                map.put(Telephony.Carriers.NUMERIC, mIccRecords.getOperatorNumeric());
+                log("updateCurrentCarrierInProvider insert uri=" + uri);
+                mContext.getContentResolver().insert(uri, map);
+                return true;
+            } catch (SQLException e) {
+                Log.e(LOG_TAG, "[CDMALTEPhone] Can't store current operator ret false", e);
+            }
+        } else {
+            log("updateCurrentCarrierInProvider mIccRecords == null ret false");
+        }
+        return false;
+    }
+
+    @Override
+    public void setSystemLocale(String language, String country, boolean fromMcc) {
+        // Avoid system locale is set from MCC table if CDMALTEPhone is used.
+        // The locale will be picked up based on EFpl/EFli once CSIM records are loaded.
+        if (fromMcc) return;
+
+        super.setSystemLocale(language, country, false);
+    }
+
+    // return IMSI from USIM as subscriber ID.
+    @Override
+    public String getSubscriberId() {
+        return mIccRecords.getIMSI();
+    }
+
+    @Override
+    protected void log(String s) {
+        if (DBG)
+            Log.d(LOG_TAG, "[CDMALTEPhone] " + s);
+    }
+}
index 74adebd..7f6bb45 100755 (executable)
@@ -49,6 +49,7 @@ import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.DataConnection;
+import com.android.internal.telephony.IccRecords;
 import com.android.internal.telephony.MccTable;
 import com.android.internal.telephony.IccCard;
 import com.android.internal.telephony.IccException;
@@ -66,6 +67,7 @@ import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.telephony.UUSInfo;
+import com.android.internal.telephony.CallTracker;
 
 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA;
 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC;
@@ -97,10 +99,7 @@ public class CDMAPhone extends PhoneBase {
 
     // Instance Variables
     CdmaCallTracker mCT;
-    CdmaSMSDispatcher mSMS;
     CdmaServiceStateTracker mSST;
-    RuimRecords mRuimRecords;
-    RuimCard mRuimCard;
     ArrayList <CdmaMmiCode> mPendingMmis = new ArrayList<CdmaMmiCode>();
     RuimPhoneBookInterfaceManager mRuimPhoneBookInterfaceManager;
     RuimSmsInterfaceManager mRuimSmsInterfaceManager;
@@ -141,34 +140,43 @@ public class CDMAPhone extends PhoneBase {
 
     // Constructors
     public CDMAPhone(Context context, CommandsInterface ci, PhoneNotifier notifier) {
-        this(context,ci,notifier, false);
+        super(notifier, context, ci, false);
+        initSstIcc();
+        init(context, notifier);
     }
 
     public CDMAPhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
             boolean unitTestMode) {
         super(notifier, context, ci, unitTestMode);
+        initSstIcc();
+        init(context, notifier);
+    }
+
+    protected void initSstIcc() {
+        mSST = new CdmaServiceStateTracker(this);
+        mIccRecords = new RuimRecords(this);
+        mIccCard = new RuimCard(this, LOG_TAG, DBG);
+        mIccFileHandler = new RuimFileHandler(this);
+    }
 
+    protected void init(Context context, PhoneNotifier notifier) {
         mCM.setPhoneType(Phone.PHONE_TYPE_CDMA);
         mCT = new CdmaCallTracker(this);
-        mSST = new CdmaServiceStateTracker (this);
         mSMS = new CdmaSMSDispatcher(this);
-        mIccFileHandler = new RuimFileHandler(this);
-        mRuimRecords = new RuimRecords(this);
-        mDataConnection = new CdmaDataConnectionTracker (this);
-        mRuimCard = new RuimCard(this);
+        mDataConnectionTracker = new CdmaDataConnectionTracker (this);
         mRuimPhoneBookInterfaceManager = new RuimPhoneBookInterfaceManager(this);
         mRuimSmsInterfaceManager = new RuimSmsInterfaceManager(this, mSMS);
         mSubInfo = new PhoneSubInfo(this);
         mEriManager = new EriManager(this, context, EriManager.ERI_FROM_XML);
-        mCcatService = CatService.getInstance(mCM, mRuimRecords, mContext,
-                mIccFileHandler, mRuimCard);
+        mCcatService = CatService.getInstance(mCM, mIccRecords, mContext,
+                mIccFileHandler, mIccCard);
 
         mCM.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
-        mRuimRecords.registerForRecordsLoaded(this, EVENT_RUIM_RECORDS_LOADED, null);
+        mIccRecords.registerForRecordsLoaded(this, EVENT_RUIM_RECORDS_LOADED, null);
         mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
         mCM.registerForOn(this, EVENT_RADIO_ON, null);
         mCM.setOnSuppServiceNotification(this, EVENT_SSN, null);
-        mSST.registerForNetworkAttach(this, EVENT_REGISTERED_TO_NETWORK, null);
+        mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null);
         mCM.registerForNVReady(this, EVENT_NV_READY, null);
         mCM.setEmergencyCallbackMode(this, EVENT_EMERGENCY_CALLBACK_MODE_ENTER, null);
 
@@ -213,14 +221,15 @@ public class CDMAPhone extends PhoneBase {
     public void dispose() {
         synchronized(PhoneProxy.lockForRadioTechnologyChange) {
             super.dispose();
+            log("dispose");
 
             //Unregister from all former registered events
-            mRuimRecords.unregisterForRecordsLoaded(this); //EVENT_RUIM_RECORDS_LOADED
+            mIccRecords.unregisterForRecordsLoaded(this); //EVENT_RUIM_RECORDS_LOADED
             mCM.unregisterForAvailable(this); //EVENT_RADIO_AVAILABLE
             mCM.unregisterForOffOrNotAvailable(this); //EVENT_RADIO_OFF_OR_NOT_AVAILABLE
             mCM.unregisterForOn(this); //EVENT_RADIO_ON
             mCM.unregisterForNVReady(this); //EVENT_NV_READY
-            mSST.unregisterForNetworkAttach(this); //EVENT_REGISTERED_TO_NETWORK
+            mSST.unregisterForNetworkAttached(this); //EVENT_REGISTERED_TO_NETWORK
             mCM.unSetOnSuppServiceNotification(this);
             removeCallbacks(mExitEcmRunnable);
 
@@ -228,12 +237,12 @@ public class CDMAPhone extends PhoneBase {
 
             //Force all referenced classes to unregister their former registered events
             mCT.dispose();
-            mDataConnection.dispose();
+            mDataConnectionTracker.dispose();
             mSST.dispose();
             mSMS.dispose();
             mIccFileHandler.dispose(); // instance of RuimFileHandler
-            mRuimRecords.dispose();
-            mRuimCard.dispose();
+            mIccRecords.dispose();
+            mIccCard.dispose();
             mRuimPhoneBookInterfaceManager.dispose();
             mRuimSmsInterfaceManager.dispose();
             mSubInfo.dispose();
@@ -243,14 +252,15 @@ public class CDMAPhone extends PhoneBase {
     }
 
     public void removeReferences() {
+            log("removeReferences");
             this.mRuimPhoneBookInterfaceManager = null;
             this.mRuimSmsInterfaceManager = null;
             this.mSMS = null;
             this.mSubInfo = null;
-            this.mRuimRecords = null;
+            this.mIccRecords = null;
             this.mIccFileHandler = null;
-            this.mRuimCard = null;
-            this.mDataConnection = null;
+            this.mIccCard = null;
+            this.mDataConnectionTracker = null;
             this.mCT = null;
             this.mSST = null;
             this.mEriManager = null;
@@ -270,10 +280,18 @@ public class CDMAPhone extends PhoneBase {
         return mSST.ss;
     }
 
+    public CallTracker getCallTracker() {
+        return mCT;
+    }
+
     public Phone.State getState() {
         return mCT.state;
     }
 
+    public ServiceStateTracker getServiceStateTracker() {
+        return mSST;
+    }
+
     public String getPhoneName() {
         return "CDMA";
     }
@@ -319,9 +337,9 @@ public class CDMAPhone extends PhoneBase {
     public DataActivityState getDataActivityState() {
         DataActivityState ret = DataActivityState.NONE;
 
-        if (mSST.getCurrentCdmaDataConnectionState() == ServiceState.STATE_IN_SERVICE) {
+        if (mSST.getCurrentDataConnectionState() == ServiceState.STATE_IN_SERVICE) {
 
-            switch (mDataConnection.getActivity()) {
+            switch (mDataConnectionTracker.getActivity()) {
                 case DATAIN:
                     ret = DataActivityState.DATAIN;
                 break;
@@ -530,14 +548,6 @@ public class CDMAPhone extends PhoneBase {
         Log.e(LOG_TAG, "setLine1Number: not possible in CDMA");
     }
 
-    public IccCard getIccCard() {
-        return mRuimCard;
-    }
-
-    public String getIccSerialNumber() {
-        return mRuimRecords.iccid;
-    }
-
     public void setCallWaiting(boolean enable, Message onComplete) {
         Log.e(LOG_TAG, "method setCallWaiting is NOT supported in CDMA!");
     }
@@ -547,7 +557,7 @@ public class CDMAPhone extends PhoneBase {
     }
 
     public void setDataRoamingEnabled(boolean enable) {
-        mDataConnection.setDataOnRoamingEnabled(enable);
+        mDataConnectionTracker.setDataOnRoamingEnabled(enable);
     }
 
     public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj) {
@@ -608,15 +618,15 @@ public class CDMAPhone extends PhoneBase {
              // already been called
 
              ret = DataState.DISCONNECTED;
-        } else if (mSST.getCurrentCdmaDataConnectionState() != ServiceState.STATE_IN_SERVICE) {
+        } else if (mSST.getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE) {
             // If we're out of service, open TCP sockets may still work
             // but no data will flow
             ret = DataState.DISCONNECTED;
-        } else if (mDataConnection.isApnTypeEnabled(apnType) == false ||
-                mDataConnection.isApnTypeActive(apnType) == false) {
+        } else if (mDataConnectionTracker.isApnTypeEnabled(apnType) == false ||
+                mDataConnectionTracker.isApnTypeActive(apnType) == false) {
             ret = DataState.DISCONNECTED;
         } else {
-            switch (mDataConnection.getState()) {
+            switch (mDataConnectionTracker.getState(apnType)) {
                 case FAILED:
                 case IDLE:
                     ret = DataState.DISCONNECTED;
@@ -625,7 +635,7 @@ public class CDMAPhone extends PhoneBase {
                 case CONNECTED:
                 case DISCONNECTING:
                     if ( mCT.state != Phone.State.IDLE
-                            && !mSST.isConcurrentVoiceAndData()) {
+                            && !mSST.isConcurrentVoiceAndDataAllowed()) {
                         ret = DataState.SUSPENDED;
                     } else {
                         ret = DataState.CONNECTED;
@@ -640,6 +650,7 @@ public class CDMAPhone extends PhoneBase {
             }
         }
 
+        log("getDataConnectionState apnType=" + apnType + " ret=" + ret);
         return ret;
     }
 
@@ -707,11 +718,7 @@ public class CDMAPhone extends PhoneBase {
     }
 
     public boolean getDataRoamingEnabled() {
-        return mDataConnection.getDataOnRoamingEnabled();
-    }
-
-    public List<DataConnection> getCurrentDataConnectionList () {
-        return mDataConnection.getAllDataConnections();
+        return mDataConnectionTracker.getDataOnRoamingEnabled();
     }
 
     public void setVoiceMailNumber(String alphaTag,
@@ -720,7 +727,7 @@ public class CDMAPhone extends PhoneBase {
         Message resp;
         mVmNumber = voiceMailNumber;
         resp = obtainMessage(EVENT_SET_VM_NUMBER_DONE, 0, 0, onComplete);
-        mRuimRecords.setVoiceMailNumber(alphaTag, mVmNumber, resp);
+        mIccRecords.setVoiceMailNumber(alphaTag, mVmNumber, resp);
     }
 
     public String getVoiceMailNumber() {
@@ -742,7 +749,7 @@ public class CDMAPhone extends PhoneBase {
      * @hide
      */
     public int getVoiceMessageCount() {
-        int voicemailCount =  mRuimRecords.getVoiceMessageCount();
+        int voicemailCount =  mIccRecords.getVoiceMessageCount();
         // If mRuimRecords.getVoiceMessageCount returns zero, then there is possibility
         // that phone was power cycled and would have lost the voicemail count.
         // So get the count from preferences.
@@ -767,10 +774,6 @@ public class CDMAPhone extends PhoneBase {
         return ret;
     }
 
-    public boolean getIccRecordsLoaded() {
-        return mRuimRecords.getRecordsLoaded();
-    }
-
     public void getCallForwardingOption(int commandInterfaceCFReason, Message onComplete) {
         Log.e(LOG_TAG, "getCallForwardingOption: not possible in CDMA");
     }
@@ -851,13 +854,13 @@ public class CDMAPhone extends PhoneBase {
     /*package*/ void
     updateMessageWaitingIndicator(boolean mwi) {
         // this also calls notifyMessageWaitingIndicator()
-        mRuimRecords.setVoiceMessageWaiting(1, mwi ? -1 : 0);
+        mIccRecords.setVoiceMessageWaiting(1, mwi ? -1 : 0);
     }
 
     /* This function is overloaded to send number of voicemails instead of sending true/false */
     /*package*/ void
     updateMessageWaitingIndicator(int mwi) {
-        mRuimRecords.setVoiceMessageWaiting(1, mwi);
+        mIccRecords.setVoiceMessageWaiting(1, mwi);
     }
 
     @Override
@@ -912,7 +915,7 @@ public class CDMAPhone extends PhoneBase {
             // send an Intent
             sendEmergencyCallbackModeChange();
             // Re-initiate data connection
-            mDataConnection.setInternalDataEnabled(true);
+            mDataConnectionTracker.setInternalDataEnabled(true);
         }
     }
 
@@ -1027,13 +1030,8 @@ public class CDMAPhone extends PhoneBase {
             case EVENT_NV_READY:{
                 Log.d(LOG_TAG, "Event EVENT_NV_READY Received");
                 //Inform the Service State Tracker
-                mEriManager.loadEriFile();
                 mNvLoadedRegistrants.notifyRegistrants();
-                if(mEriManager.isEriFileLoaded()) {
-                    // when the ERI file is loaded
-                    Log.d(LOG_TAG, "ERI read, notify registrants");
-                    mEriFileLoadedRegistrants.notifyRegistrants();
-                }
+                prepareEri();
             }
             break;
 
@@ -1404,6 +1402,7 @@ public class CDMAPhone extends PhoneBase {
                 Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current");
                 ContentValues map = new ContentValues();
                 map.put(Telephony.Carriers.NUMERIC, operatorNumeric);
+                log("updateCurrentCarrierInProvider insert uri=" + uri);
                 getContext().getContentResolver().insert(uri, map);
 
                 // Updates MCC MNC device configuration information
@@ -1416,4 +1415,22 @@ public class CDMAPhone extends PhoneBase {
         }
         return false;
     }
+
+    public void prepareEri() {
+        mEriManager.loadEriFile();
+        if(mEriManager.isEriFileLoaded()) {
+            // when the ERI file is loaded
+            log("ERI read, notify registrants");
+            mEriFileLoadedRegistrants.notifyRegistrants();
+        }
+    }
+
+    public boolean isEriFileLoaded() {
+        return mEriManager.isEriFileLoaded();
+    }
+
+    protected void log(String s) {
+        if (DBG)
+            Log.d(LOG_TAG, "[CDMAPhone] " + s);
+    }
 }
index a89f783..db19321 100644 (file)
@@ -134,6 +134,10 @@ public final class CdmaCallTracker extends CallTracker {
     public void registerForVoiceCallStarted(Handler h, int what, Object obj) {
         Registrant r = new Registrant(h, what, obj);
         voiceCallStartedRegistrants.add(r);
+        // Notify if in call when registering
+        if (state != Phone.State.IDLE) {
+            r.notifyRegistrant(new AsyncResult(null, null, null));
+        }
     }
     public void unregisterForVoiceCallStarted(Handler h) {
         voiceCallStartedRegistrants.remove(h);
@@ -1058,7 +1062,7 @@ public final class CdmaCallTracker extends CallTracker {
         if (PhoneNumberUtils.isEmergencyNumber(dialString)) {
             if (Phone.DEBUG_PHONE) log("disableDataCallInEmergencyCall");
             mIsInEmergencyCall = true;
-            phone.mDataConnection.setInternalDataEnabled(false);
+            phone.mDataConnectionTracker.setInternalDataEnabled(false);
         }
     }
 
@@ -1075,7 +1079,7 @@ public final class CdmaCallTracker extends CallTracker {
             }
             if (inEcm.compareTo("false") == 0) {
                 // Re-initiate data connection
-                phone.mDataConnection.setInternalDataEnabled(true);
+                phone.mDataConnectionTracker.setInternalDataEnabled(true);
             }
         }
     }
index fbe455e..1a15393 100755 (executable)
@@ -430,7 +430,7 @@ public class CdmaConnection extends Connection {
                 } else if (serviceState == ServiceState.STATE_OUT_OF_SERVICE
                         || serviceState == ServiceState.STATE_EMERGENCY_ONLY) {
                     return DisconnectCause.OUT_OF_SERVICE;
-                } else if (phone.mCM.getRadioState() != CommandsInterface.RadioState.NV_READY
+                } else if (phone.mCM.getNvState() != CommandsInterface.RadioState.NV_READY
                         && phone.getIccCard().getState() != RuimCard.State.READY) {
                     return DisconnectCause.ICC_ERROR;
                 } else if (causeCode==CallFailCause.NORMAL_CLEARING) {
index 1a0dbc2..d55f346 100644 (file)
@@ -20,7 +20,6 @@ import android.os.Message;
 import android.util.Log;
 
 import com.android.internal.telephony.DataConnection;
-import com.android.internal.telephony.DataConnection.FailCause;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.RetryManager;
@@ -33,8 +32,8 @@ public class CdmaDataConnection extends DataConnection {
     private static final String LOG_TAG = "CDMA";
 
     // ***** Constructor
-    private CdmaDataConnection(CDMAPhone phone, String name, RetryManager rm) {
-        super(phone, name, rm);
+    private CdmaDataConnection(CDMAPhone phone, String name, int id, RetryManager rm) {
+        super(phone, name, id, rm);
     }
 
     /**
@@ -49,11 +48,10 @@ public class CdmaDataConnection extends DataConnection {
         synchronized (mCountLock) {
             mCount += 1;
         }
-        CdmaDataConnection cdmaDc = new CdmaDataConnection(phone,
-                "CdmaDataConnection-" + mCount, rm);
+        CdmaDataConnection cdmaDc = new CdmaDataConnection(phone, "CdmaDC-" + mCount,
+                id, rm);
         cdmaDc.start();
         if (DBG) cdmaDc.log("Made " + cdmaDc.getName());
-        cdmaDc.mId = id;
         return cdmaDc;
     }
 
@@ -68,6 +66,7 @@ public class CdmaDataConnection extends DataConnection {
     protected void onConnect(ConnectionParams cp) {
         if (DBG) log("CdmaDataConnection Connecting...");
 
+        mApn = cp.apn;
         createTime = -1;
         lastFailTime = -1;
         lastFailCause = FailCause.NONE;
@@ -84,7 +83,7 @@ public class CdmaDataConnection extends DataConnection {
         Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
         msg.obj = cp;
         phone.mCM.setupDataCall(
-                Integer.toString(RILConstants.SETUP_DATA_TECH_CDMA),
+                Integer.toString(getRadioTechnology(RILConstants.SETUP_DATA_TECH_CDMA)),
                 Integer.toString(dataProfile),
                 null, null, null,
                 Integer.toString(RILConstants.SETUP_DATA_AUTH_PAP_CHAP),
@@ -99,9 +98,9 @@ public class CdmaDataConnection extends DataConnection {
 
     @Override
     protected boolean isDnsOk(String[] domainNameServers) {
-        if ((NULL_IP.equals(domainNameServers[0])
+        if (NULL_IP.equals(domainNameServers[0])
                 && NULL_IP.equals(domainNameServers[1])
-                && !((CDMAPhone) phone).isDnsCheckDisabled())) {
+                && !phone.isDnsCheckDisabled()) {
             return false;
         } else {
             return true;
index 8c36106..800615c 100644 (file)
@@ -37,10 +37,12 @@ import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.DataCallState;
 import com.android.internal.telephony.DataConnection.FailCause;
 import com.android.internal.telephony.DataConnection;
+import com.android.internal.telephony.DataConnectionAc;
 import com.android.internal.telephony.DataConnectionTracker;
 import com.android.internal.telephony.EventLogTags;
 import com.android.internal.telephony.RetryManager;
 import com.android.internal.telephony.Phone;
+import com.android.internal.util.AsyncChannel;
 
 import java.util.ArrayList;
 
@@ -52,9 +54,6 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
 
     private CDMAPhone mCdmaPhone;
 
-    //useful for debugging
-    boolean mFailNextConnect = false;
-
     /** The DataConnection being setup */
     private CdmaDataConnection mPendingDataConnection;
 
@@ -99,13 +98,13 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
 
         p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null);
         p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
-        p.mRuimRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);
+        p.mIccRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);
         p.mCM.registerForNVReady(this, EVENT_NV_READY, null);
         p.mCM.registerForDataNetworkStateChanged (this, EVENT_DATA_STATE_CHANGED, null);
         p.mCT.registerForVoiceCallEnded (this, EVENT_VOICE_CALL_ENDED, null);
         p.mCT.registerForVoiceCallStarted (this, EVENT_VOICE_CALL_STARTED, null);
-        p.mSST.registerForCdmaDataConnectionAttached(this, EVENT_TRY_SETUP_DATA, null);
-        p.mSST.registerForCdmaDataConnectionDetached(this, EVENT_CDMA_DATA_DETACHED, null);
+        p.mSST.registerForDataConnectionAttached(this, EVENT_TRY_SETUP_DATA, null);
+        p.mSST.registerForDataConnectionDetached(this, EVENT_CDMA_DATA_DETACHED, null);
         p.mSST.registerForRoamingOn(this, EVENT_ROAMING_ON, null);
         p.mSST.registerForRoamingOff(this, EVENT_ROAMING_OFF, null);
         p.mCM.registerForCdmaOtaProvision(this, EVENT_CDMA_OTA_PROVISION, null);
@@ -118,18 +117,20 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
 
     @Override
     public void dispose() {
+        cleanUpConnection(false, null);
+
         super.dispose();
 
         // Unregister from all events
         mPhone.mCM.unregisterForAvailable(this);
         mPhone.mCM.unregisterForOffOrNotAvailable(this);
-        mCdmaPhone.mRuimRecords.unregisterForRecordsLoaded(this);
+        mCdmaPhone.mIccRecords.unregisterForRecordsLoaded(this);
         mPhone.mCM.unregisterForNVReady(this);
         mPhone.mCM.unregisterForDataNetworkStateChanged(this);
         mCdmaPhone.mCT.unregisterForVoiceCallEnded(this);
         mCdmaPhone.mCT.unregisterForVoiceCallStarted(this);
-        mCdmaPhone.mSST.unregisterForCdmaDataConnectionAttached(this);
-        mCdmaPhone.mSST.unregisterForCdmaDataConnectionDetached(this);
+        mCdmaPhone.mSST.unregisterForDataConnectionAttached(this);
+        mCdmaPhone.mSST.unregisterForDataConnectionDetached(this);
         mCdmaPhone.mSST.unregisterForRoamingOn(this);
         mCdmaPhone.mSST.unregisterForRoamingOff(this);
         mPhone.mCM.unregisterForCdmaOtaProvision(this);
@@ -158,6 +159,11 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
     }
 
     @Override
+    public synchronized State getState(String apnType) {
+        return mState;
+    }
+
+    @Override
     protected boolean isApnTypeAvailable(String type) {
         for (String s : mSupportedApnTypes) {
             if (TextUtils.equals(type, s)) {
@@ -167,42 +173,18 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
         return false;
     }
 
-    /**
-     * The data connection is expected to be setup while device
-     *  1. has ruim card or non-volatile data store
-     *  2. registered to data connection 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.
-     */
-    @Override
-    public boolean isDataConnectionAsDesired() {
-        boolean roaming = mPhone.getServiceState().getRoaming();
-
-        if (((mPhone.mCM.getRadioState() == CommandsInterface.RadioState.NV_READY) ||
-                 mCdmaPhone.mRuimRecords.getRecordsLoaded()) &&
-                (mCdmaPhone.mSST.getCurrentCdmaDataConnectionState() ==
-                 ServiceState.STATE_IN_SERVICE) &&
-                (!roaming || getDataOnRoamingEnabled()) &&
-                !mIsWifiConnected ) {
-            return (mState == State.CONNECTED);
-        }
-        return true;
-    }
-
     @Override
     protected boolean isDataAllowed() {
-        int psState = mCdmaPhone.mSST.getCurrentCdmaDataConnectionState();
+        int psState = mCdmaPhone.mSST.getCurrentDataConnectionState();
         boolean roaming = (mPhone.getServiceState().getRoaming() && !getDataOnRoamingEnabled());
         boolean desiredPowerState = mCdmaPhone.mSST.getDesiredPowerState();
 
         boolean allowed =
                     (psState == ServiceState.STATE_IN_SERVICE ||
                             mAutoAttachOnCreation) &&
-                    (mPhone.mCM.getRadioState() == CommandsInterface.RadioState.NV_READY ||
-                            mCdmaPhone.mRuimRecords.getRecordsLoaded()) &&
-                    (mCdmaPhone.mSST.isConcurrentVoiceAndData() ||
+                    (mPhone.mCM.getNvState() == CommandsInterface.RadioState.NV_READY ||
+                            mCdmaPhone.mIccRecords.getRecordsLoaded()) &&
+                    (mCdmaPhone.mSST.isConcurrentVoiceAndDataAllowed() ||
                             mPhone.getState() == Phone.State.IDLE) &&
                     !roaming &&
                     mInternalDataEnabled &&
@@ -214,11 +196,11 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
             if (!((psState == ServiceState.STATE_IN_SERVICE) || mAutoAttachOnCreation)) {
                 reason += " - psState= " + psState;
             }
-            if (!(mPhone.mCM.getRadioState() == CommandsInterface.RadioState.NV_READY ||
-                    mCdmaPhone.mRuimRecords.getRecordsLoaded())) {
-                reason += " - radioState= " + mPhone.mCM.getRadioState() + " - RUIM not loaded";
+            if (!(mPhone.mCM.getNvState() == CommandsInterface.RadioState.NV_READY ||
+                    mCdmaPhone.mIccRecords.getRecordsLoaded())) {
+                reason += " - radioState= " + mPhone.mCM.getNvState() + " - RUIM not loaded";
             }
-            if (!(mCdmaPhone.mSST.isConcurrentVoiceAndData() ||
+            if (!(mCdmaPhone.mSST.isConcurrentVoiceAndDataAllowed() ||
                     mPhone.getState() == Phone.State.IDLE)) {
                 reason += " - concurrentVoiceAndData not allowed and state= " + mPhone.getState();
             }
@@ -232,6 +214,16 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
         return allowed;
     }
 
+    @Override
+    protected boolean isDataPossible(String apnType) {
+        boolean possible = isDataAllowed() && !(getAnyDataEnabled() &&
+                (mState == State.FAILED || mState == State.IDLE));
+        if (!possible && DBG && isDataAllowed()) {
+            log("Data not possible.  No coverage: dataState = " + mState);
+        }
+        return possible;
+    }
+
     private boolean trySetupData(String reason) {
         if (DBG) log("***trySetupData due to " + (reason == null ? "(unspecified)" : reason));
 
@@ -246,7 +238,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
             return true;
         }
 
-        int psState = mCdmaPhone.mSST.getCurrentCdmaDataConnectionState();
+        int psState = mCdmaPhone.mSST.getCurrentDataConnectionState();
         boolean roaming = mPhone.getServiceState().getRoaming();
         boolean desiredPowerState = mCdmaPhone.mSST.getDesiredPowerState();
 
@@ -262,9 +254,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
     }
 
     /**
-     * Cleanup all connections.
-     *
-     * TODO: Cleanup only a specified connection passed as a parameter.
+     * Cleanup the CDMA data connection (only one is supported)
      *
      * @param tearDown true if the underlying DataConnection should be disconnected.
      * @param reason for the clean up.
@@ -286,14 +276,18 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
         boolean notificationDeferred = false;
         for (DataConnection conn : mDataConnections.values()) {
             if(conn != null) {
+                DataConnectionAc dcac =
+                    mDataConnectionAsyncChannels.get(conn.getDataConnectionId());
                 if (tearDown) {
                     if (DBG) log("cleanUpConnection: teardown, call conn.disconnect");
-                    conn.disconnect(obtainMessage(EVENT_DISCONNECT_DONE,
+                    conn.tearDown(reason, obtainMessage(EVENT_DISCONNECT_DONE,
                             conn.getDataConnectionId(), 0, reason));
                     notificationDeferred = true;
                 } else {
                     if (DBG) log("cleanUpConnection: !tearDown, call conn.resetSynchronously");
-                    conn.resetSynchronously();
+                    if (dcac != null) {
+                        dcac.resetSync();
+                    }
                     notificationDeferred = false;
                 }
             }
@@ -308,11 +302,13 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
     }
 
     private CdmaDataConnection findFreeDataConnection() {
-        for (DataConnection dc : mDataConnections.values()) {
-            if (dc.isInactive()) {
-                return (CdmaDataConnection) dc;
+        for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
+            if (dcac.isInactiveSync()) {
+                log("found free GsmDataConnection");
+                return (CdmaDataConnection) dcac.dataConnection;
             }
         }
+        log("NO free CdmaDataConnection");
         return null;
     }
 
@@ -338,12 +334,12 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
         }
         mActiveApn = new ApnSetting(apnId, "", "", "", "", "", "", "", "", "",
                                     "", 0, types, "IP", "IP");
-        if (DBG) log("setupData: mActiveApn=" + mActiveApn);
+        if (DBG) log("call conn.bringUp mActiveApn=" + mActiveApn);
 
         Message msg = obtainMessage();
         msg.what = EVENT_DATA_SETUP_COMPLETE;
         msg.obj = reason;
-        conn.connect(msg, mActiveApn);
+        conn.bringUp(msg, mActiveApn);
 
         setState(State.INITING);
         notifyDataConnection(reason);
@@ -354,7 +350,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
         setState(State.CONNECTED);
         notifyDataConnection(reason);
         startNetStatPoll();
-        mRetryMgr.resetRetryCount();
+        mDataConnections.get(0).resetRetryCount();
     }
 
     private void resetPollStats() {
@@ -386,7 +382,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
     protected void restartRadio() {
         if (DBG) log("Cleanup connection and wait " +
                 (TIME_DELAYED_TO_RESTART_RADIO / 1000) + "s to restart radio");
-        cleanUpConnection(true, Phone.REASON_RADIO_TURNED_OFF);
+        cleanUpAllConnections(null);
         sendEmptyMessageDelayed(EVENT_RESTART_RADIO, TIME_DELAYED_TO_RESTART_RADIO);
         mPendingRestartRadio = true;
     }
@@ -504,21 +500,9 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
              * at the last time until the state is changed.
              * TODO: Make this configurable?
              */
-            int nextReconnectDelay = mRetryMgr.getRetryTimer();
-            log("Data Connection activate failed. Scheduling next attempt for "
-                    + (nextReconnectDelay / 1000) + "s");
-
-            AlarmManager am =
-                (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
-            Intent intent = new Intent(INTENT_RECONNECT_ALARM);
-            intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason);
-            mReconnectIntent = PendingIntent.getBroadcast(
-                    mPhone.getContext(), 0, intent, 0);
-            am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                    SystemClock.elapsedRealtime() + nextReconnectDelay,
-                    mReconnectIntent);
-
-            mRetryMgr.increaseRetryCount();
+            int nextReconnectDelay = mDataConnections.get(0).getRetryTimer();
+            startAlarmForReconnect(nextReconnectDelay, reason);
+            mDataConnections.get(0).increaseRetryCount();
 
             if (!shouldPostNotification(lastFailCauseCode)) {
                 log("NOT Posting Data Connection Unavailable notification "
@@ -529,12 +513,28 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
         }
     }
 
+    private void startAlarmForReconnect(int delay, String reason) {
+
+        log("Data Connection activate failed. Scheduling next attempt for "
+                + (delay / 1000) + "s");
+
+        AlarmManager am =
+            (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
+        Intent intent = new Intent(INTENT_RECONNECT_ALARM);
+        intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason);
+        mReconnectIntent = PendingIntent.getBroadcast(
+                mPhone.getContext(), 0, intent, 0);
+        am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                SystemClock.elapsedRealtime() + delay, mReconnectIntent);
+
+    }
+
     private void notifyNoData(FailCause lastFailCauseCode) {
         setState(State.FAILED);
         notifyDataAvailability(null);
     }
 
-    private void gotoIdleAndNotifyDataConnection(String reason) {
+    protected void gotoIdleAndNotifyDataConnection(String reason) {
         if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason);
         setState(State.IDLE);
         notifyDataConnection(reason);
@@ -543,14 +543,14 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
 
     protected void onRecordsLoaded() {
         if (mState == State.FAILED) {
-            cleanUpConnection(false, null);
+            cleanUpAllConnections(null);
         }
         sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, Phone.REASON_SIM_LOADED));
     }
 
     protected void onNVReady() {
         if (mState == State.FAILED) {
-            cleanUpConnection(false, null);
+            cleanUpAllConnections(null);
         }
         sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA));
     }
@@ -560,6 +560,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
      */
     @Override
     protected void onEnableNewApn() {
+        // No mRequestedApnType check; only one connection is supported
         cleanUpConnection(true, Phone.REASON_APN_SWITCHED);
     }
 
@@ -588,7 +589,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
             trySetupData(Phone.REASON_ROAMING_ON);
         } else {
             if (DBG) log("Tear down data connection on roaming.");
-            cleanUpConnection(true, Phone.REASON_ROAMING_ON);
+            cleanUpAllConnections(null);
         }
     }
 
@@ -609,7 +610,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
         notifyDataAvailability(null);
 
         if (mState != State.IDLE) {
-            cleanUpConnection(true, null);
+            cleanUpAllConnections(null);
         }
     }
 
@@ -618,7 +619,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
      */
     @Override
     protected void onRadioOffOrNotAvailable() {
-        mRetryMgr.resetRetryCount();
+        mDataConnections.get(0).resetRetryCount();
 
         if (mPhone.getSimulatedRadioControl() != null) {
             // Assume data is connected on the simulator
@@ -626,7 +627,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
             log("We're on the simulator; assuming radio off is meaningless");
         } else {
             if (DBG) log("Radio is off and clean up all connection");
-            cleanUpConnection(false, Phone.REASON_RADIO_TURNED_OFF);
+            cleanUpAllConnections(null);
         }
     }
 
@@ -640,12 +641,8 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
             reason = (String) ar.userObj;
         }
 
-        if (ar.exception == null) {
-            // TODO: We should clear LinkProperties/Capabilities when torn down or disconnected
-            mLinkProperties = getLinkProperties(mPendingDataConnection);
-            mLinkCapabilities = getLinkCapabilities(mPendingDataConnection);
-
-            // everything is setup
+        if (isDataSetupCompleteOk(ar)) {
+            // Everything is setup
             notifyDefaultData(reason);
         } else {
             FailCause cause = (FailCause) (ar.result);
@@ -688,30 +685,17 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
         notifyDataConnection(reason);
         mActiveApn = null;
         if (retryAfterDisconnected(reason)) {
-          trySetupData(reason);
+          // Wait a bit before trying, so we're not tying up RIL command channel.
+          startAlarmForReconnect(APN_DELAY_MILLIS, reason);
       }
     }
 
     /**
-     * Called when EVENT_RESET_DONE is received so goto
-     * IDLE state and send notifications to those interested.
-     */
-    @Override
-    protected void onResetDone(AsyncResult ar) {
-      if (DBG) log("EVENT_RESET_DONE");
-      String reason = null;
-      if (ar.userObj instanceof String) {
-          reason = (String) ar.userObj;
-      }
-      gotoIdleAndNotifyDataConnection(reason);
-    }
-
-    /**
      * @override com.android.internal.telephony.DataConnectionTracker
      */
     @Override
     protected void onVoiceCallStarted() {
-        if (mState == State.CONNECTED && !mCdmaPhone.mSST.isConcurrentVoiceAndData()) {
+        if (mState == State.CONNECTED && !mCdmaPhone.mSST.isConcurrentVoiceAndDataAllowed()) {
             stopNetStatPoll();
             notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
             notifyDataAvailability(Phone.REASON_VOICE_CALL_STARTED);
@@ -724,7 +708,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
     @Override
     protected void onVoiceCallEnded() {
         if (mState == State.CONNECTED) {
-            if (!mCdmaPhone.mSST.isConcurrentVoiceAndData()) {
+            if (!mCdmaPhone.mSST.isConcurrentVoiceAndDataAllowed()) {
                 startNetStatPoll();
                 notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
             } else {
@@ -733,36 +717,52 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
             }
             notifyDataAvailability(Phone.REASON_VOICE_CALL_ENDED);
         } else {
-            mRetryMgr.resetRetryCount();
+            mDataConnections.get(0).resetRetryCount();
             // in case data setup was attempted when we were on a voice call
             trySetupData(Phone.REASON_VOICE_CALL_ENDED);
         }
     }
 
     @Override
-    protected void onCleanUpConnection(boolean tearDown, String reason) {
+    protected void onCleanUpConnection(boolean tearDown, int apnId, String reason) {
+        // No apnId check; only one connection is supported
         cleanUpConnection(tearDown, reason);
     }
 
+    @Override
+    protected void onCleanUpAllConnections(String cause) {
+        // Only one CDMA connection is supported
+        cleanUpConnection(true, cause);
+    }
+
     private void createAllDataConnectionList() {
         CdmaDataConnection dataConn;
 
-        /** TODO: Use one retry manager for all connections for now */
-        RetryManager rm = mRetryMgr;
-        if (!rm.configure(SystemProperties.get("ro.cdma.data_retry_config"))) {
-            if (!rm.configure(DEFAULT_DATA_RETRY_CONFIG)) {
-                // Should never happen, log an error and default to a simple linear sequence.
-                log("Could not configure using DEFAULT_DATA_RETRY_CONFIG="
-                        + DEFAULT_DATA_RETRY_CONFIG);
-                rm.configure(20, 2000, 1000);
+        String retryConfig = SystemProperties.get("ro.cdma.data_retry_config");
+        for (int i = 0; i < DATA_CONNECTION_POOL_SIZE; i++) {
+            RetryManager rm = new RetryManager();
+            if (!rm.configure(retryConfig)) {
+                if (!rm.configure(DEFAULT_DATA_RETRY_CONFIG)) {
+                    // Should never happen, log an error and default to a simple linear sequence.
+                    log("Could not configure using DEFAULT_DATA_RETRY_CONFIG="
+                            + DEFAULT_DATA_RETRY_CONFIG);
+                    rm.configure(20, 2000, 1000);
+                }
             }
-        }
 
-        for (int i = 0; i < DATA_CONNECTION_POOL_SIZE; i++) {
             int id = mUniqueIdGenerator.getAndIncrement();
-
             dataConn = CdmaDataConnection.makeDataConnection(mCdmaPhone, id, rm);
             mDataConnections.put(id, dataConn);
+            DataConnectionAc dcac = new DataConnectionAc(dataConn, LOG_TAG);
+            int status = dcac.fullyConnectSync(mPhone.getContext(), this, dataConn.getHandler());
+            if (status == AsyncChannel.STATUS_SUCCESSFUL) {
+                log("Fully connected");
+                mDataConnectionAsyncChannels.put(dcac.dataConnection.getDataConnectionId(), dcac);
+            } else {
+                log("Could not connect to dcac.dataConnection=" + dcac.dataConnection +
+                        " status=" + status);
+            }
+
         }
     }
 
@@ -779,7 +779,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
         } else {
             if (mState == State.FAILED) {
                 cleanUpConnection(false, Phone.REASON_CDMA_DATA_DETACHED);
-                mRetryMgr.resetRetryCount();
+                mDataConnections.get(0).resetRetryCount();
 
                 CdmaCellLocation loc = (CdmaCellLocation)(mPhone.getCellLocation());
                 EventLog.writeEvent(EventLogTags.CDMA_DATA_SETUP_FAILED,
@@ -797,7 +797,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
                 switch (otaPrivision[0]) {
                 case Phone.CDMA_OTA_PROVISION_STATUS_COMMITTED:
                 case Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STOPPED:
-                    mRetryMgr.resetRetryCount();
+                    mDataConnections.get(0).resetRetryCount();
                     break;
                 default:
                     break;
@@ -893,8 +893,9 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
 
     @Override
     public void handleMessage (Message msg) {
+        if (DBG) log("CdmaDCT handleMessage msg=" + msg);
 
-        if (!mPhone.mIsTheCurrentActivePhone) {
+        if (!mPhone.mIsTheCurrentActivePhone || mIsDisposed) {
             log("Ignore CDMA msgs since CDMA phone is inactive");
             return;
         }
@@ -933,12 +934,17 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
     }
 
     @Override
+    public boolean isAnyActiveDataConnections() {
+        return (mState != State.IDLE);
+    }
+
+    @Override
     protected void log(String s) {
-        Log.d(LOG_TAG, "[CdmaDataConnectionTracker] " + s);
+        Log.d(LOG_TAG, "[CdmaDCT] " + s);
     }
 
     @Override
     protected void loge(String s) {
-        Log.e(LOG_TAG, "[CdmaDataConnectionTracker] " + s);
+        Log.e(LOG_TAG, "[CdmaDCT] " + s);
     }
 }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
new file mode 100644 (file)
index 0000000..459cf87
--- /dev/null
@@ -0,0 +1,516 @@
+/*
+ * 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 com.android.internal.telephony.cdma;
+
+import com.android.internal.telephony.TelephonyProperties;
+import com.android.internal.telephony.MccTable;
+import com.android.internal.telephony.EventLogTags;
+import com.android.internal.telephony.RILConstants;
+
+import android.content.Intent;
+import android.telephony.SignalStrength;
+import android.telephony.ServiceState;
+import android.telephony.cdma.CdmaCellLocation;
+import android.os.AsyncResult;
+import android.os.Message;
+import android.provider.Telephony.Intents;
+
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.EventLog;
+
+import com.android.internal.telephony.gsm.GsmDataConnectionTracker;
+
+public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker {
+    CDMALTEPhone mCdmaLtePhone;
+
+    private ServiceState  mLteSS;  // The last LTE state from Voice Registration
+    private String mCurrentSpn = null;
+
+    public CdmaLteServiceStateTracker(CDMALTEPhone phone) {
+        super(phone);
+        cm.registerForSIMReady(this, EVENT_SIM_READY, null);
+        mCdmaLtePhone = phone;
+
+        mLteSS = new ServiceState();
+        if (DBG) log("CdmaLteServiceStateTracker Constructors");
+    }
+
+    @Override
+    public void dispose() {
+        cm.unregisterForSIMReady(this);
+        super.dispose();
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        AsyncResult ar;
+        int[] ints;
+        String[] strings;
+        switch (msg.what) {
+        case EVENT_POLL_STATE_GPRS:
+            if (DBG) log("handleMessage EVENT_POLL_STATE_GPRS");
+            ar = (AsyncResult)msg.obj;
+            handlePollStateResult(msg.what, ar);
+            break;
+        case EVENT_SIM_READY:
+            if (DBG) log("handleMessage EVENT_SIM_READY");
+            isSubscriptionFromRuim = false;
+            // Register SIM_RECORDS_LOADED dynamically.
+            // This is to avoid confilct with RUIM_READY scenario)
+            phone.mIccRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
+            pollState();
+            // Signal strength polling stops when radio is off.
+            queueNextSignalStrengthPoll();
+
+            // load ERI file
+            phone.prepareEri();
+            break;
+        case EVENT_SIM_RECORDS_LOADED:
+            CdmaLteUiccRecords sim = (CdmaLteUiccRecords)phone.mIccRecords;
+            if ((sim != null) && sim.isProvisioned()) {
+                mMdn = sim.getMdn();
+                mMin = sim.getMin();
+                parseSidNid(sim.getSid(), sim.getNid());
+                mPrlVersion = sim.getPrlVersion();;
+                mIsMinInfoReady = true;
+                updateOtaspState();
+            }
+            // SID/NID/PRL is loaded. Poll service state
+            // again to update to the roaming state with
+            // the latest variables.
+            pollState();
+            break;
+        default:
+            super.handleMessage(msg);
+        }
+    }
+
+    /**
+     * Set the cdmaSS for EVENT_POLL_STATE_REGISTRATION_CDMA
+     */
+    @Override
+    protected void setCdmaTechnology(int radioTechnology) {
+        // Called on voice registration state response.
+        // Just record new CDMA radio technology
+        newSS.setRadioTechnology(radioTechnology);
+    }
+
+    /**
+     * Handle the result of one of the pollState()-related requests
+     */
+    @Override
+    protected void handlePollStateResultMessage(int what, AsyncResult ar) {
+        if (what == EVENT_POLL_STATE_GPRS) {
+            if (DBG) log("handlePollStateResultMessage: EVENT_POLL_STATE_GPRS");
+            String states[] = (String[])ar.result;
+
+            int type = 0;
+            int regState = -1;
+            if (states.length > 0) {
+                try {
+                    regState = Integer.parseInt(states[0]);
+
+                    // states[3] (if present) is the current radio technology
+                    if (states.length >= 4 && states[3] != null) {
+                        type = Integer.parseInt(states[3]);
+                    }
+                } catch (NumberFormatException ex) {
+                    loge("handlePollStateResultMessage: error parsing GprsRegistrationState: "
+                                    + ex);
+                }
+            }
+
+            // Not sure if this is needed in CDMALTE phone.
+            // mDataRoaming = regCodeIsRoaming(regState);
+            mLteSS.setRadioTechnology(type);
+            mLteSS.setState(regCodeToServiceState(regState));
+        } else {
+            super.handlePollStateResultMessage(what, ar);
+        }
+    }
+
+    @Override
+    protected void setSignalStrengthDefaultValues() {
+        mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, false);
+    }
+
+    @Override
+    protected void pollState() {
+        pollingContext = new int[1];
+        pollingContext[0] = 0;
+
+        switch (cm.getRadioState()) {
+            case RADIO_UNAVAILABLE:
+                newSS.setStateOutOfService();
+                newCellLoc.setStateInvalid();
+                setSignalStrengthDefaultValues();
+                mGotCountryCode = false;
+
+                pollStateDone();
+                break;
+
+            case RADIO_OFF:
+                newSS.setStateOff();
+                newCellLoc.setStateInvalid();
+                setSignalStrengthDefaultValues();
+                mGotCountryCode = false;
+
+                pollStateDone();
+                break;
+
+            default:
+                // Issue all poll-related commands at once, then count
+                // down the responses which are allowed to arrive
+                // out-of-order.
+
+                pollingContext[0]++;
+                // RIL_REQUEST_OPERATOR is necessary for CDMA
+                cm.getOperator(obtainMessage(EVENT_POLL_STATE_OPERATOR_CDMA, pollingContext));
+
+                pollingContext[0]++;
+                // RIL_REQUEST_VOICE_REGISTRATION_STATE is necessary for CDMA
+                cm.getVoiceRegistrationState(obtainMessage(EVENT_POLL_STATE_REGISTRATION_CDMA,
+                        pollingContext));
+
+                int networkMode = android.provider.Settings.Secure.getInt(phone.getContext()
+                        .getContentResolver(),
+                        android.provider.Settings.Secure.PREFERRED_NETWORK_MODE,
+                        RILConstants.PREFERRED_NETWORK_MODE);
+                if (DBG) log("pollState: network mode here is = " + networkMode);
+                if ((networkMode == RILConstants.NETWORK_MODE_GLOBAL)
+                        || (networkMode == RILConstants.NETWORK_MODE_LTE_ONLY)) {
+                    pollingContext[0]++;
+                    // RIL_REQUEST_DATA_REGISTRATION_STATE
+                    cm.getDataRegistrationState(obtainMessage(EVENT_POLL_STATE_GPRS,
+                                                pollingContext));
+                }
+                break;
+        }
+    }
+
+    @Override
+    protected void pollStateDone() {
+        // determine data NetworkType from both LET and CDMA SS
+        if (mLteSS.getState() == ServiceState.STATE_IN_SERVICE) {
+            //in LTE service
+            newNetworkType = mLteSS.getRadioTechnology();
+            mNewDataConnectionState = mLteSS.getState();
+            newSS.setRadioTechnology(newNetworkType);
+            log("pollStateDone LTE/eHRPD STATE_IN_SERVICE newNetworkType = " + newNetworkType);
+        } else {
+            // LTE out of service, get CDMA Service State
+            newNetworkType = newSS.getRadioTechnology();
+            mNewDataConnectionState = radioTechnologyToDataServiceState(newNetworkType);
+            log("pollStateDone CDMA STATE_IN_SERVICE newNetworkType = " + newNetworkType +
+                " mNewDataConnectionState = " + mNewDataConnectionState);
+        }
+
+        if (DBG) log("pollStateDone: oldSS=[" + ss + "] newSS=[" + newSS + "]");
+
+        boolean hasRegistered = ss.getState() != ServiceState.STATE_IN_SERVICE
+                && newSS.getState() == ServiceState.STATE_IN_SERVICE;
+
+        boolean hasDeregistered = ss.getState() == ServiceState.STATE_IN_SERVICE
+                && newSS.getState() != ServiceState.STATE_IN_SERVICE;
+
+        boolean hasCdmaDataConnectionAttached =
+            mDataConnectionState != ServiceState.STATE_IN_SERVICE
+                && mNewDataConnectionState == ServiceState.STATE_IN_SERVICE;
+
+        boolean hasCdmaDataConnectionDetached =
+            mDataConnectionState == ServiceState.STATE_IN_SERVICE
+                && mNewDataConnectionState != ServiceState.STATE_IN_SERVICE;
+
+        boolean hasCdmaDataConnectionChanged =
+            mDataConnectionState != mNewDataConnectionState;
+
+        boolean hasNetworkTypeChanged = networkType != newNetworkType;
+
+        boolean hasChanged = !newSS.equals(ss);
+
+        boolean hasRoamingOn = !ss.getRoaming() && newSS.getRoaming();
+
+        boolean hasRoamingOff = ss.getRoaming() && !newSS.getRoaming();
+
+        boolean hasLocationChanged = !newCellLoc.equals(cellLoc);
+
+        boolean has4gHandoff =
+                ((networkType == ServiceState.RADIO_TECHNOLOGY_LTE) &&
+                 (newNetworkType == ServiceState.RADIO_TECHNOLOGY_EHRPD)) ||
+                ((networkType == ServiceState.RADIO_TECHNOLOGY_EHRPD) &&
+                 (newNetworkType == ServiceState.RADIO_TECHNOLOGY_LTE));
+
+        boolean hasMultiApnSupport =
+                (((newNetworkType == ServiceState.RADIO_TECHNOLOGY_LTE) ||
+                  (newNetworkType == ServiceState.RADIO_TECHNOLOGY_EHRPD)) &&
+                 ((networkType != ServiceState.RADIO_TECHNOLOGY_LTE) &&
+                  (networkType != ServiceState.RADIO_TECHNOLOGY_EHRPD)));
+
+        boolean hasLostMultiApnSupport =
+            ((newNetworkType >= ServiceState.RADIO_TECHNOLOGY_IS95A) &&
+             (newNetworkType <= ServiceState.RADIO_TECHNOLOGY_EVDO_A));
+
+        if (DBG) {
+            log("pollStateDone:"
+                + " hasRegistered=" + hasRegistered
+                + " hasDeegistered=" + hasDeregistered
+                + " hasCdmaDataConnectionAttached=" + hasCdmaDataConnectionAttached
+                + " hasCdmaDataConnectionDetached=" + hasCdmaDataConnectionDetached
+                + " hasCdmaDataConnectionChanged=" + hasCdmaDataConnectionChanged
+                + " hasNetworkTypeChanged = " + hasNetworkTypeChanged
+                + " hasChanged=" + hasChanged
+                + " hasRoamingOn=" + hasRoamingOn
+                + " hasRoamingOff=" + hasRoamingOff
+                + " hasLocationChanged=" + hasLocationChanged
+                + " has4gHandoff = " + has4gHandoff
+                + " hasMultiApnSupport=" + hasMultiApnSupport
+                + " hasLostMultiApnSupport=" + hasLostMultiApnSupport);
+        }
+        // Add an event log when connection state changes
+        if (ss.getState() != newSS.getState()
+                || mDataConnectionState != mNewDataConnectionState) {
+            EventLog.writeEvent(EventLogTags.CDMA_SERVICE_STATE_CHANGE, ss.getState(),
+                    mDataConnectionState, newSS.getState(), mNewDataConnectionState);
+        }
+
+        ServiceState tss;
+        tss = ss;
+        ss = newSS;
+        newSS = tss;
+        // clean slate for next time
+        newSS.setStateOutOfService();
+        mLteSS.setStateOutOfService();
+
+        if ((hasMultiApnSupport)
+                && (phone.mDataConnectionTracker instanceof CdmaDataConnectionTracker)) {
+            if (DBG) log("GsmDataConnectionTracker Created");
+            phone.mDataConnectionTracker.dispose();
+            phone.mDataConnectionTracker = new GsmDataConnectionTracker(mCdmaLtePhone);
+        }
+
+        if ((hasLostMultiApnSupport)
+                && (phone.mDataConnectionTracker instanceof GsmDataConnectionTracker)) {
+            if (DBG)log("GsmDataConnectionTracker disposed");
+            phone.mDataConnectionTracker.dispose();
+            phone.mDataConnectionTracker = new CdmaDataConnectionTracker(phone);
+        }
+
+        CdmaCellLocation tcl = cellLoc;
+        cellLoc = newCellLoc;
+        newCellLoc = tcl;
+
+        mDataConnectionState = mNewDataConnectionState;
+        networkType = newNetworkType;
+
+        newSS.setStateOutOfService(); // clean slate for next time
+
+        if (hasNetworkTypeChanged) {
+            phone.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
+                    ServiceState.radioTechnologyToString(networkType));
+        }
+
+        if (hasRegistered) {
+            mNetworkAttachedRegistrants.notifyRegistrants();
+        }
+
+        if (hasChanged) {
+            if (phone.isEriFileLoaded()) {
+                String eriText;
+                // Now the CDMAPhone sees the new ServiceState so it can get the
+                // new ERI text
+                if (ss.getState() == ServiceState.STATE_IN_SERVICE) {
+                    eriText = phone.getCdmaEriText();
+                } else {
+                    // Note that ServiceState.STATE_OUT_OF_SERVICE is valid used
+                    // for
+                    // mRegistrationState 0,2,3 and 4
+                    eriText = phone.getContext()
+                            .getText(com.android.internal.R.string.roamingTextSearching).toString();
+                }
+                ss.setOperatorAlphaLong(eriText);
+            }
+
+            String operatorNumeric;
+
+            phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ALPHA,
+                    ss.getOperatorAlphaLong());
+
+            operatorNumeric = ss.getOperatorNumeric();
+            phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, operatorNumeric);
+
+            if (operatorNumeric == null) {
+                phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
+            } else {
+                String isoCountryCode = "";
+                try {
+                    isoCountryCode = MccTable.countryCodeForMcc(Integer.parseInt(operatorNumeric
+                            .substring(0, 3)));
+                } catch (NumberFormatException ex) {
+                    loge("countryCodeForMcc error" + ex);
+                } catch (StringIndexOutOfBoundsException ex) {
+                    loge("countryCodeForMcc error" + ex);
+                }
+
+                phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY,
+                        isoCountryCode);
+                mGotCountryCode = true;
+                if (mNeedFixZone) {
+                    fixTimeZone(isoCountryCode);
+                }
+            }
+
+            phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING,
+                    ss.getRoaming() ? "true" : "false");
+
+            updateSpnDisplay();
+            phone.notifyServiceStateChanged(ss);
+        }
+
+        if (hasCdmaDataConnectionAttached) {
+            mAttachedRegistrants.notifyRegistrants();
+        }
+
+        if (hasCdmaDataConnectionDetached) {
+            mDetachedRegistrants.notifyRegistrants();
+        }
+
+        if ((hasCdmaDataConnectionChanged || hasNetworkTypeChanged)) {
+            phone.notifyDataConnection();
+        }
+
+        if (hasRoamingOn) {
+            mRoamingOnRegistrants.notifyRegistrants();
+        }
+
+        if (hasRoamingOff) {
+            mRoamingOffRegistrants.notifyRegistrants();
+        }
+
+        if (hasLocationChanged) {
+            phone.notifyLocationChanged();
+        }
+    }
+
+    @Override
+    protected void onSignalStrengthResult(AsyncResult ar) {
+        SignalStrength oldSignalStrength = mSignalStrength;
+
+        if (ar.exception != null) {
+            // Most likely radio is resetting/disconnected change to default
+            // values.
+            setSignalStrengthDefaultValues();
+        } else {
+            int[] ints = (int[])ar.result;
+            int lteCqi = 99, lteRsrp = -1;
+            int lteRssi = 99;
+            int offset = 2;
+            int cdmaDbm = (ints[offset] > 0) ? -ints[offset] : -120;
+            int cdmaEcio = (ints[offset + 1] > 0) ? -ints[offset + 1] : -160;
+            int evdoRssi = (ints[offset + 2] > 0) ? -ints[offset + 2] : -120;
+            int evdoEcio = (ints[offset + 3] > 0) ? -ints[offset + 3] : -1;
+            int evdoSnr = ((ints[offset + 4] > 0) && (ints[offset + 4] <= 8)) ? ints[offset + 4]
+                    : -1;
+            if (networkType == ServiceState.RADIO_TECHNOLOGY_LTE) {
+                lteRssi = (ints[offset + 5] >= 0) ? ints[offset + 5] : 99;
+                lteRsrp = (ints[offset + 6] < 0) ? ints[offset + 6] : -1;
+                lteCqi = (ints[offset + 7] >= 0) ? ints[offset + 7] : 99;
+            }
+
+            if (networkType != ServiceState.RADIO_TECHNOLOGY_LTE) {
+                mSignalStrength = new SignalStrength(99, -1, cdmaDbm, cdmaEcio, evdoRssi, evdoEcio,
+                        evdoSnr, false);
+            } else {
+                mSignalStrength = new SignalStrength(99, -1, cdmaDbm, cdmaEcio, evdoRssi, evdoEcio,
+                        evdoSnr, lteRssi, lteRsrp, -1, -1, lteCqi, true);
+            }
+        }
+
+        try {
+            phone.notifySignalStrength();
+        } catch (NullPointerException ex) {
+            loge("onSignalStrengthResult() Phone already destroyed: " + ex
+                    + "SignalStrength not notified");
+        }
+    }
+
+    @Override
+    public boolean isConcurrentVoiceAndDataAllowed() {
+        // Note: it needs to be confirmed which CDMA network types
+        // can support voice and data calls concurrently.
+        // For the time-being, the return value will be false.
+        // return (networkType >= ServiceState.RADIO_TECHNOLOGY_LTE);
+        return false;
+    }
+
+    /**
+     * Returns OTASP_NOT_NEEDED as its not needed for LTE
+     */
+    @Override
+    int getOtasp() {
+        int provisioningState = OTASP_NOT_NEEDED;
+        if (DBG) log("getOtasp: state=" + provisioningState);
+        return provisioningState;
+    }
+
+    @Override
+    protected void updateSpnDisplay() {
+        // mOperatorAlphaLong contains the ERI text
+        String plmn = ss.getOperatorAlphaLong();
+
+        boolean showSpn = false;
+        String spn = null;
+        if (cm.getSimState().isSIMReady()) {
+            // SIM is found on the device. Read the operator name from the card.
+            showSpn = ((CdmaLteUiccRecords)phone.mIccRecords).getCsimSpnDisplayCondition();
+            spn = phone.mIccRecords.getServiceProviderName();
+
+            // double check we are not printing identicall test
+            if (TextUtils.equals(plmn, spn)) showSpn = false;
+        }
+
+        if (!TextUtils.equals(plmn, mCurPlmn) ||
+            !TextUtils.equals(spn, mCurrentSpn)) {
+            boolean showPlmn = plmn != null;
+            if (DBG) {
+                log(String.format("updateSpnDisplay: changed sending intent" +
+                                  " showPlmn='%b' plmn='%s' showSpn='%b' spn='%s'",
+                                  showPlmn, plmn, showSpn, spn));
+            }
+            Intent intent = new Intent(Intents.SPN_STRINGS_UPDATED_ACTION);
+            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+            intent.putExtra(Intents.EXTRA_SHOW_SPN, showSpn);
+            intent.putExtra(Intents.EXTRA_SPN, spn);
+            intent.putExtra(Intents.EXTRA_SHOW_PLMN, showPlmn);
+            intent.putExtra(Intents.EXTRA_PLMN, plmn);
+            phone.getContext().sendStickyBroadcast(intent);
+        }
+
+        mCurPlmn = plmn;
+        mCurrentSpn = spn;
+    }
+
+    @Override
+    protected void log(String s) {
+        Log.d(LOG_TAG, "[CdmaLteSST] " + s);
+    }
+
+    @Override
+    protected void loge(String s) {
+        Log.e(LOG_TAG, "[CdmaLteSST] " + s);
+    }
+}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java
new file mode 100644 (file)
index 0000000..b9d7c46
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2011 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.cdma;
+
+import android.util.Log;
+import com.android.internal.telephony.IccConstants;
+import com.android.internal.telephony.IccFileHandler;
+import android.os.Message;
+
+/**
+ * {@hide}
+ */
+public final class CdmaLteUiccFileHandler extends IccFileHandler {
+    static final String LOG_TAG = "CDMA";
+
+    CdmaLteUiccFileHandler(CDMALTEPhone phone) {
+        super(phone);
+    }
+
+    protected String getEFPath(int efid) {
+        switch(efid) {
+        case EF_CSIM_SPN:
+        case EF_CSIM_LI:
+        case EF_CSIM_MDN:
+        case EF_CSIM_IMSIM:
+        case EF_CSIM_CDMAHOME:
+        case EF_CSIM_EPRL:
+            return MF_SIM + DF_CDMA;
+        case EF_AD:
+            return MF_SIM + DF_GSM;
+        }
+        return getCommonIccEFPath(efid);
+    }
+
+    @Override
+    public void loadEFTransparent(int fileid, Message onLoaded) {
+        if (fileid == EF_CSIM_EPRL) {
+            // Entire PRL could be huge. We are only interested in
+            // the first 4 bytes of the record.
+            phone.mCM.iccIO(COMMAND_READ_BINARY, fileid, getEFPath(fileid),
+                            0, 0, 4, null, null,
+                            obtainMessage(EVENT_READ_BINARY_DONE,
+                                          fileid, 0, onLoaded));
+        } else {
+            super.loadEFTransparent(fileid, onLoaded);
+        }
+    }
+
+
+    protected void logd(String msg) {
+        Log.d(LOG_TAG, "[CdmaLteUiccFileHandler] " + msg);
+    }
+
+    protected void loge(String msg) {
+        Log.e(LOG_TAG, "[CdmaLteUiccFileHandler] " + msg);
+    }
+
+}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java
new file mode 100755 (executable)
index 0000000..10515f7
--- /dev/null
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) 2011 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.cdma;
+
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA;
+import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.telephony.IccCardApplication.AppType;
+import com.android.internal.telephony.IccFileHandler;
+import com.android.internal.telephony.IccUtils;
+import com.android.internal.telephony.MccTable;
+import com.android.internal.telephony.PhoneBase;
+import com.android.internal.telephony.cdma.sms.UserData;
+import com.android.internal.telephony.gsm.SIMRecords;
+import android.os.AsyncResult;
+import android.os.Message;
+import android.os.SystemProperties;
+import android.util.Log;
+import java.util.Locale;
+import java.util.ArrayList;
+
+/**
+ * {@hide}
+ */
+public final class CdmaLteUiccRecords extends SIMRecords {
+    // From CSIM application
+    private byte[] mEFpl = null;
+    private byte[] mEFli = null;
+    boolean mCsimSpnDisplayCondition = false;
+    private String mMdn;
+    private String mMin;
+    private String mPrlVersion;
+    private String mHomeSystemId;
+    private String mHomeNetworkId;
+
+    private static final int EVENT_GET_PL_DONE = CSIM_EVENT_BASE;
+    private static final int EVENT_GET_CSIM_LI_DONE = CSIM_EVENT_BASE + 1;
+    private static final int EVENT_GET_CSIM_SPN_DONE = CSIM_EVENT_BASE + 2;
+    private static final int EVENT_GET_CSIM_MDN_DONE = CSIM_EVENT_BASE + 3;
+    private static final int EVENT_GET_CSIM_IMSIM_DONE = CSIM_EVENT_BASE + 4;
+    private static final int EVENT_GET_CSIM_CDMAHOME_DONE = CSIM_EVENT_BASE + 5;
+    private static final int EVENT_GET_CSIM_EPRL_DONE = CSIM_EVENT_BASE + 6;
+
+    public CdmaLteUiccRecords(PhoneBase p) {
+        super(p);
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        AsyncResult ar;
+        byte data[];
+
+        boolean isCsimRecordLoadResponse = false;
+
+        try { switch (msg.what) {
+            case EVENT_GET_PL_DONE:
+                // Refer to ETSI TS.102.221
+                if (DBG) log("EF_GET_EF_PL_DONE");
+                isCsimRecordLoadResponse = true;
+
+                ar = (AsyncResult) msg.obj;
+
+                if (ar.exception != null) {
+                    Log.e(LOG_TAG, "ar.exception = " + ar.exception);
+                    break;
+                }
+
+                mEFpl = (byte[]) ar.result;
+                if (DBG) log("EF_PL=" + IccUtils.bytesToHexString(mEFpl));
+                break;
+
+            case EVENT_GET_CSIM_LI_DONE:
+                // Refer to C.S0065 5.2.26
+                if (DBG) log("EVENT_GET_CSIM_LI_DONE");
+                isCsimRecordLoadResponse = true;
+
+                ar = (AsyncResult) msg.obj;
+                if (ar.exception != null) {
+                    Log.e(LOG_TAG, "ar.exception = " + ar.exception);
+                    break;
+                }
+
+                mEFli = (byte[]) ar.result;
+                // convert csim efli data to iso 639 format
+                for (int i = 0; i < mEFli.length; i+=2) {
+                    switch(mEFli[i+1]) {
+                    case 0x01: mEFli[i] = 'e'; mEFli[i+1] = 'n';break;
+                    case 0x02: mEFli[i] = 'f'; mEFli[i+1] = 'r';break;
+                    case 0x03: mEFli[i] = 'e'; mEFli[i+1] = 's';break;
+                    case 0x04: mEFli[i] = 'j'; mEFli[i+1] = 'a';break;
+                    case 0x05: mEFli[i] = 'k'; mEFli[i+1] = 'o';break;
+                    case 0x06: mEFli[i] = 'z'; mEFli[i+1] = 'h';break;
+                    case 0x07: mEFli[i] = 'h'; mEFli[i+1] = 'e';break;
+                    default: mEFli[i] = ' '; mEFli[i+1] = ' ';
+                    }
+                }
+
+                if (DBG) log("EF_LI=" + IccUtils.bytesToHexString(mEFli));
+                break;
+            case EVENT_GET_CSIM_SPN_DONE:
+                // Refer to C.S0065 5.2.32
+                if (DBG) log("EVENT_GET_CSIM_SPN_DONE");
+                isCsimRecordLoadResponse = true;
+                ar = (AsyncResult) msg.obj;
+
+                if (ar.exception != null) {
+                    Log.e(LOG_TAG, "ar.exception=" + ar.exception);
+                    break;
+                }
+                onGetCSimSpnDone(ar);
+                break;
+            case EVENT_GET_CSIM_MDN_DONE:
+                if (DBG) log("EVENT_GET_CSIM_MDN_DONE");
+                isCsimRecordLoadResponse = true;
+                ar = (AsyncResult) msg.obj;
+                if (ar.exception != null) {
+                    Log.e(LOG_TAG, "ar.exception=" + ar.exception);
+                    break;
+                }
+                onGetCSimMdnDone(ar);
+                break;
+            case EVENT_GET_CSIM_IMSIM_DONE:
+                if (DBG) log("EVENT_GET_CSIM_IMSIM_DONE");
+                isCsimRecordLoadResponse = true;
+                ar = (AsyncResult) msg.obj;
+                if (ar.exception != null) {
+                    Log.e(LOG_TAG, "ar.exception=" + ar.exception);
+                    break;
+                }
+                onGetCSimImsimDone(ar);
+                break;
+            case EVENT_GET_CSIM_CDMAHOME_DONE:
+                if (DBG) log("EVENT_GET_CSIM_CDMAHOME_DONE");
+                isCsimRecordLoadResponse = true;
+                ar = (AsyncResult) msg.obj;
+                if (ar.exception != null) {
+                    Log.e(LOG_TAG, "ar.exception=" + ar.exception);
+                    break;
+                }
+                onGetCSimCdmaHomeDone(ar);
+                break;
+            case EVENT_GET_CSIM_EPRL_DONE:
+                if (DBG) log("EVENT_GET_CSIM_EPRL_DONE");
+                isCsimRecordLoadResponse = true;
+                ar = (AsyncResult) msg.obj;
+                if (ar.exception != null) {
+                    Log.e(LOG_TAG, "ar.exception=" + ar.exception);
+                    break;
+                }
+                onGetCSimEprlDone(ar);
+                break;
+            default:
+                super.handleMessage(msg);
+        }}catch (RuntimeException exc) {
+            Log.w(LOG_TAG, "Exception parsing SIM record", exc);
+        } finally {
+            if (isCsimRecordLoadResponse) {
+                onRecordLoaded();
+            }
+        }
+    }
+
+    @Override
+    protected void onRecordLoaded() {
+        // One record loaded successfully or failed, In either case
+        // we need to update the recordsToLoad count
+        recordsToLoad -= 1;
+
+        if (recordsToLoad == 0 && recordsRequested == true) {
+            onAllRecordsLoaded();
+        } else if (recordsToLoad < 0) {
+            Log.e(LOG_TAG, "SIMRecords: recordsToLoad <0, programmer error suspected");
+            recordsToLoad = 0;
+        }
+    }
+
+    @Override
+    protected void onAllRecordsLoaded() {
+        super.onAllRecordsLoaded();
+        setLocaleFromCsim();
+    }
+
+    @Override
+    protected void fetchSimRecords() {
+        IccFileHandler iccFh = phone.getIccFileHandler();
+        recordsRequested = true;
+
+        phone.mCM.getIMSI(obtainMessage(EVENT_GET_IMSI_DONE));
+        recordsToLoad++;
+
+        iccFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE));
+        recordsToLoad++;
+
+        iccFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE));
+        recordsToLoad++;
+
+        iccFh.loadEFTransparent(EF_PL, obtainMessage(EVENT_GET_PL_DONE));
+        recordsToLoad++;
+
+        iccFh.loadEFTransparent(EF_CSIM_LI, obtainMessage(EVENT_GET_CSIM_LI_DONE));
+        recordsToLoad++;
+
+        iccFh.loadEFTransparent(EF_CSIM_SPN, obtainMessage(EVENT_GET_CSIM_SPN_DONE));
+        recordsToLoad++;
+
+        iccFh.loadEFLinearFixed(EF_CSIM_MDN, 1, obtainMessage(EVENT_GET_CSIM_MDN_DONE));
+        recordsToLoad++;
+
+        iccFh.loadEFTransparent(EF_CSIM_IMSIM, obtainMessage(EVENT_GET_CSIM_IMSIM_DONE));
+        recordsToLoad++;
+
+        iccFh.loadEFLinearFixedAll(EF_CSIM_CDMAHOME,
+                                   obtainMessage(EVENT_GET_CSIM_CDMAHOME_DONE));
+        recordsToLoad++;
+
+        iccFh.loadEFTransparent(EF_CSIM_EPRL, obtainMessage(EVENT_GET_CSIM_EPRL_DONE));
+        recordsToLoad++;
+    }
+
+    private void onGetCSimSpnDone(AsyncResult ar) {
+        byte[] data = (byte[]) ar.result;
+        if (DBG) log("CSIM_SPN=" +
+                     IccUtils.bytesToHexString(data));
+
+        // C.S0065 for EF_SPN decoding
+        mCsimSpnDisplayCondition = ((0x01 & data[0]) != 0) ? true : false;
+
+        int encoding = data[1];
+        int language = data[2];
+        byte[] spnData = new byte[32];
+        System.arraycopy(data, 3, spnData, 0, (data.length < 32)?data.length:32);
+
+        int numBytes;
+        for (numBytes = 0; numBytes < spnData.length; numBytes++) {
+            if ((spnData[numBytes] & 0xFF) == 0xFF) break;
+        }
+
+        if (numBytes == 0) {
+            spn = "";
+            return;
+        }
+        try {
+            switch (encoding) {
+            case UserData.ENCODING_OCTET:
+            case UserData.ENCODING_LATIN:
+                spn = new String(spnData, 0, numBytes, "ISO-8859-1");
+                break;
+            case UserData.ENCODING_IA5:
+            case UserData.ENCODING_GSM_7BIT_ALPHABET:
+            case UserData.ENCODING_7BIT_ASCII:
+                spn = GsmAlphabet.gsm7BitPackedToString(spnData, 0, (numBytes*8)/7);
+                break;
+            case UserData.ENCODING_UNICODE_16:
+                spn =  new String(spnData, 0, numBytes, "utf-16");
+                break;
+            default:
+                log("SPN encoding not supported");
+            }
+        } catch(Exception e) {
+            log("spn decode error: " + e);
+        }
+        if (DBG) log("spn=" + spn);
+        if (DBG) log("spnCondition=" + mCsimSpnDisplayCondition);
+        phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn);
+    }
+
+    private void onGetCSimMdnDone(AsyncResult ar) {
+        byte[] data = (byte[]) ar.result;
+        if (DBG) log("CSIM_MDN=" + IccUtils.bytesToHexString(data));
+        int mdnDigitsNum = 0x0F & data[0];
+        mMdn = IccUtils.cdmaBcdToString(data, 1, mdnDigitsNum);
+        if (DBG) log("CSIM MDN=" + mMdn);
+    }
+
+    private void onGetCSimImsimDone(AsyncResult ar) {
+        byte[] data = (byte[]) ar.result;
+        if (DBG) log("CSIM_IMSIM=" + IccUtils.bytesToHexString(data));
+        // C.S0065 section 5.2.2 for IMSI_M encoding
+        // C.S0005 section 2.3.1 for MIN encoding in IMSI_M.
+        boolean provisioned = ((data[7] & 0x80) == 0x80);
+
+        if (provisioned) {
+            int first3digits = ((0x03 & data[2]) << 8) + (0xFF & data[1]);
+            int second3digits = (((0xFF & data[5]) << 8) | (0xFF & data[4])) >> 6;
+            int digit7 = 0x0F & (data[4] >> 2);
+            if (digit7 > 0x09) digit7 = 0;
+            int last3digits = ((0x03 & data[4]) << 8) | (0xFF & data[3]);
+            first3digits = adjstMinDigits(first3digits);
+            second3digits = adjstMinDigits(second3digits);
+            last3digits = adjstMinDigits(last3digits);
+
+            StringBuilder builder = new StringBuilder();
+            builder.append(String.format(Locale.US, "%03d", first3digits));
+            builder.append(String.format(Locale.US, "%03d", second3digits));
+            builder.append(String.format(Locale.US, "%d", digit7));
+            builder.append(String.format(Locale.US, "%03d", last3digits));
+            if (DBG) log("min present=" + builder.toString());
+
+            mMin = builder.toString();
+        } else {
+            if (DBG) log("min not present");
+        }
+    }
+
+    private int adjstMinDigits (int digits) {
+        // Per C.S0005 section 2.3.1.
+        digits += 111;
+        digits = (digits % 10 == 0)?(digits - 10):digits;
+        digits = ((digits / 10) % 10 == 0)?(digits - 100):digits;
+        digits = ((digits / 100) % 10 == 0)?(digits - 1000):digits;
+        return digits;
+    }
+
+    private void onGetCSimCdmaHomeDone(AsyncResult ar) {
+        // Per C.S0065 section 5.2.8
+        ArrayList<byte[]> dataList = (ArrayList<byte[]>) ar.result;
+        if (DBG) log("CSIM_CDMAHOME data size=" + dataList.size());
+        if (dataList.isEmpty()) {
+            return;
+        }
+        StringBuilder sidBuf = new StringBuilder();
+        StringBuilder nidBuf = new StringBuilder();
+
+        for (byte[] data : dataList) {
+            if (data.length == 5) {
+                int sid = ((data[1] & 0xFF) << 8) | (data[0] & 0xFF);
+                int nid = ((data[3] & 0xFF) << 8) | (data[2] & 0xFF);
+                sidBuf.append(sid).append(",");
+                nidBuf.append(nid).append(",");
+            }
+        }
+        // remove trailing ","
+        sidBuf.setLength(sidBuf.length()-1);
+        nidBuf.setLength(nidBuf.length()-1);
+
+        mHomeSystemId = sidBuf.toString();
+        mHomeNetworkId = nidBuf.toString();
+    }
+
+    private void onGetCSimEprlDone(AsyncResult ar) {
+        // C.S0065 section 5.2.57 for EFeprl encoding
+        // C.S0016 section 3.5.5 for PRL format.
+        byte[] data = (byte[]) ar.result;
+        if (DBG) log("CSIM_EPRL=" + IccUtils.bytesToHexString(data));
+
+        // Only need the first 4 bytes of record
+        if (data.length > 3) {
+            int prlId = ((data[2] & 0xFF) << 8) | (data[3] & 0xFF);
+            mPrlVersion = Integer.toString(prlId);
+        }
+        if (DBG) log("CSIM PRL version=" + mPrlVersion);
+    }
+
+    private void setLocaleFromCsim() {
+        String prefLang = null;
+        // check EFli then EFpl
+        prefLang = findBestLanguage(mEFli);
+
+        if (prefLang == null) {
+            prefLang = findBestLanguage(mEFpl);
+        }
+
+        if (prefLang != null) {
+            // check country code from SIM
+            String imsi = getIMSI();
+            String country = null;
+            if (imsi != null) {
+                country = MccTable.countryCodeForMcc(
+                                    Integer.parseInt(imsi.substring(0,3)));
+            }
+            log("Setting locale to " + prefLang + "_" + country);
+            phone.setSystemLocale(prefLang, country, false);
+        } else {
+            log ("No suitable CSIM selected locale");
+        }
+    }
+
+    private String findBestLanguage(byte[] languages) {
+        String bestMatch = null;
+        String[] locales = phone.getContext().getAssets().getLocales();
+
+        if ((languages == null) || (locales == null)) return null;
+
+        // Each 2-bytes consists of one language
+        for (int i = 0; (i + 1) < languages.length; i += 2) {
+            try {
+                String lang = new String(languages, i, 2, "ISO-8859-1");
+                for (int j = 0; j < locales.length; j++) {
+                    if (locales[j] != null && locales[j].length() >= 2 &&
+                        locales[j].substring(0, 2).equals(lang)) {
+                        return lang;
+                    }
+                }
+                if (bestMatch != null) break;
+            } catch(java.io.UnsupportedEncodingException e) {
+                log ("Failed to parse SIM language records");
+            }
+        }
+        // no match found. return null
+        return null;
+    }
+
+    @Override
+    protected void log(String s) {
+        if (DBG) Log.d(LOG_TAG, "[CSIM] " + s);
+    }
+
+    public String getMdn() {
+        return mMdn;
+    }
+
+    public String getMin() {
+        return mMin;
+    }
+
+    public String getSid() {
+        return mHomeSystemId;
+    }
+
+    public String getNid() {
+        return mHomeNetworkId;
+    }
+
+    public String getPrlVersion() {
+        return mPrlVersion;
+    }
+
+    public boolean getCsimSpnDisplayCondition() {
+        return mCsimSpnDisplayCondition;
+    }
+
+    @Override
+    public boolean isProvisioned() {
+        // If UICC card has CSIM app, look for MDN and MIN field
+        // to determine if the SIM is provisioned.  Otherwise,
+        // consider the SIM is provisioned. (for case of ordinal
+        // USIM only UICC.)
+        if (phone.mIccCard.isApplicationOnIcc(AppType.APPTYPE_CSIM) &&
+            ((mMdn == null) || (mMin == null))) {
+            return false;
+        }
+        return true;
+    }
+}
index 6bd2d09..c0bfd23 100644 (file)
@@ -94,7 +94,7 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
 
     /** {@inheritDoc} */
     @Override
-    protected int dispatchMessage(SmsMessageBase smsb) {
+    public int dispatchMessage(SmsMessageBase smsb) {
 
         // If sms is null, means there was a parsing error.
         if (smsb == null) {
@@ -485,19 +485,19 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
 
     /** {@inheritDoc} */
     @Override
-    protected void activateCellBroadcastSms(int activate, Message response) {
+    public void activateCellBroadcastSms(int activate, Message response) {
         mCm.setCdmaBroadcastActivation((activate == 0), response);
     }
 
     /** {@inheritDoc} */
     @Override
-    protected void getCellBroadcastSmsConfig(Message response) {
+    public void getCellBroadcastSmsConfig(Message response) {
         mCm.getCdmaBroadcastConfig(response);
     }
 
     /** {@inheritDoc} */
     @Override
-    protected void setCellBroadcastConfig(int[] configValuesArray, Message response) {
+    public void setCellBroadcastConfig(int[] configValuesArray, Message response) {
         mCm.setCdmaBroadcastConfig(configValuesArray, response);
     }
 
index 0debb42..24a468a 100755 (executable)
@@ -16,8 +16,6 @@
 
 package com.android.internal.telephony.cdma;
 
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC;
-
 import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.DataConnectionTracker;
@@ -61,7 +59,7 @@ import java.util.TimeZone;
 /**
  * {@hide}
  */
-final class CdmaServiceStateTracker extends ServiceStateTracker {
+public class CdmaServiceStateTracker extends ServiceStateTracker {
     static final String LOG_TAG = "CDMA";
 
     CDMAPhone phone;
@@ -86,10 +84,10 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
             NITZ_UPDATE_DIFF_DEFAULT);
 
     /**
-     *  Values correspond to ServiceStateTracker.DATA_ACCESS_ definitions.
+     *  Values correspond to ServiceState.RADIO_TECHNOLOGY_ definitions.
      */
-    private int networkType = 0;
-    private int newNetworkType = 0;
+    protected int networkType = 0;
+    protected int newNetworkType = 0;
 
     private boolean mCdmaRoaming = false;
     private int mRoamingIndicator;
@@ -99,23 +97,21 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
     /**
      * Initially assume no data connection.
      */
-    private int cdmaDataConnectionState = ServiceState.STATE_OUT_OF_SERVICE;
-    private int newCdmaDataConnectionState = ServiceState.STATE_OUT_OF_SERVICE;
-    private int mRegistrationState = -1;
-    private RegistrantList cdmaDataConnectionAttachedRegistrants = new RegistrantList();
-    private RegistrantList cdmaDataConnectionDetachedRegistrants = new RegistrantList();
-    private RegistrantList cdmaForSubscriptionInfoReadyRegistrants = new RegistrantList();
+    protected int mDataConnectionState = ServiceState.STATE_OUT_OF_SERVICE;
+    protected int mNewDataConnectionState = ServiceState.STATE_OUT_OF_SERVICE;
+    protected int mRegistrationState = -1;
+    protected RegistrantList cdmaForSubscriptionInfoReadyRegistrants = new RegistrantList();
 
     /**
      * Sometimes we get the NITZ time before we know what country we
      * are in. Keep the time zone information from the NITZ string so
      * we can fix the time zone once know the country.
      */
-    private boolean mNeedFixZone = false;
+    protected boolean mNeedFixZone = false;
     private int mZoneOffset;
     private boolean mZoneDst;
     private long mZoneTime;
-    private boolean mGotCountryCode = false;
+    protected boolean mGotCountryCode = false;
     String mSavedTimeZone;
     long mSavedTime;
     long mSavedAtTime;
@@ -131,19 +127,17 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
     private static final String WAKELOCK_TAG = "ServiceStateTracker";
 
     /** Contains the name of the registered network in CDMA (either ONS or ERI text). */
-    private String curPlmn = null;
+    protected String mCurPlmn = null;
 
-    private String mMdn;
+    protected String mMdn;
     private int mHomeSystemId[] = null;
     private int mHomeNetworkId[] = null;
-    private String mMin;
-    private String mPrlVersion;
-    private boolean mIsMinInfoReady = false;
+    protected String mMin;
+    protected String mPrlVersion;
+    protected boolean mIsMinInfoReady = false;
 
     private boolean isEriTextLoaded = false;
-    private boolean isSubscriptionFromRuim = false;
-
-    private boolean mPendingRadioPowerOffAfterDataOff = false;
+    protected boolean isSubscriptionFromRuim = false;
 
     /* Used only for debugging purposes. */
     private String mRegistrationDeniedReason;
@@ -154,7 +148,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
     private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) {
         @Override
         public void onChange(boolean selfChange) {
-            Log.i("CdmaServiceStateTracker", "Auto time state changed");
+            if (DBG) log("Auto time state changed");
             revertToNitzTime();
         }
     };
@@ -162,7 +156,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
     private ContentObserver mAutoTimeZoneObserver = new ContentObserver(new Handler()) {
         @Override
         public void onChange(boolean selfChange) {
-            Log.i("CdmaServiceStateTracker", "Auto time zone state changed");
+            if (DBG) log("Auto time zone state changed");
             revertToNitzTimeZone();
         }
     };
@@ -220,11 +214,11 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
         cm.unregisterForNVReady(this);
         cm.unregisterForCdmaOtaProvision(this);
         phone.unregisterForEriFileLoaded(this);
-        phone.mRuimRecords.unregisterForRecordsLoaded(this);
+        phone.mIccRecords.unregisterForRecordsLoaded(this);
         cm.unSetOnSignalStrengthUpdate(this);
         cm.unSetOnNITZTime(this);
-        cr.unregisterContentObserver(this.mAutoTimeObserver);
-        cr.unregisterContentObserver(this.mAutoTimeZoneObserver);
+        cr.unregisterContentObserver(mAutoTimeObserver);
+        cr.unregisterContentObserver(mAutoTimeZoneObserver);
     }
 
     @Override
@@ -232,57 +226,6 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
         if (DBG) log("CdmaServiceStateTracker finalized");
     }
 
-    void registerForNetworkAttach(Handler h, int what, Object obj) {
-        Registrant r = new Registrant(h, what, obj);
-        networkAttachedRegistrants.add(r);
-
-        if (ss.getState() == ServiceState.STATE_IN_SERVICE) {
-            r.notifyRegistrant();
-        }
-    }
-
-    void unregisterForNetworkAttach(Handler h) {
-        networkAttachedRegistrants.remove(h);
-    }
-
-    /**
-     * Registration point for transition into Data attached.
-     * @param h handler to notify
-     * @param what what code of message when delivered
-     * @param obj placed in Message.obj
-     */
-    void registerForCdmaDataConnectionAttached(Handler h, int what, Object obj) {
-        Registrant r = new Registrant(h, what, obj);
-        cdmaDataConnectionAttachedRegistrants.add(r);
-
-        if (cdmaDataConnectionState == ServiceState.STATE_IN_SERVICE) {
-            r.notifyRegistrant();
-        }
-    }
-
-    void unregisterForCdmaDataConnectionAttached(Handler h) {
-        cdmaDataConnectionAttachedRegistrants.remove(h);
-    }
-
-    /**
-     * Registration point for transition into Data detached.
-     * @param h handler to notify
-     * @param what what code of message when delivered
-     * @param obj placed in Message.obj
-     */
-    void registerForCdmaDataConnectionDetached(Handler h, int what, Object obj) {
-        Registrant r = new Registrant(h, what, obj);
-        cdmaDataConnectionDetachedRegistrants.add(r);
-
-        if (cdmaDataConnectionState != ServiceState.STATE_IN_SERVICE) {
-            r.notifyRegistrant();
-        }
-    }
-
-    void unregisterForCdmaDataConnectionDetached(Handler h) {
-        cdmaDataConnectionDetachedRegistrants.remove(h);
-    }
-
     /**
      * Registration point for subscription info ready
      * @param h handler to notify
@@ -310,6 +253,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
 
         switch (msg.what) {
         case EVENT_RADIO_AVAILABLE:
+            if (DBG) log("handleMessage: EVENT_RADIO_AVAILABLE");
             break;
 
         case EVENT_RUIM_READY:
@@ -317,13 +261,13 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
             // unlocked. At this stage, the radio is already powered on.
             isSubscriptionFromRuim = true;
             if (mNeedToRegForRuimLoaded) {
-                phone.mRuimRecords.registerForRecordsLoaded(this,
+                phone.mIccRecords.registerForRecordsLoaded(this,
                         EVENT_RUIM_RECORDS_LOADED, null);
                 mNeedToRegForRuimLoaded = false;
             }
 
             cm.getCDMASubscription(obtainMessage(EVENT_POLL_STATE_CDMA_SUBSCRIPTION));
-            if (DBG) log("Receive EVENT_RUIM_READY and Send Request getCDMASubscription.");
+            if (DBG) log("handleMessage: EVENT_RUIM_READY, Send Request getCDMASubscription.");
 
             // Restore the previous network selection.
             pollState();
@@ -337,6 +281,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
             // For Non-RUIM phones, the subscription information is stored in
             // Non Volatile. Here when Non-Volatile is ready, we can poll the CDMA
             // subscription info.
+            if (DBG) log("handleMessage: EVENT_NV_READY, Send Request getCDMASubscription.");
             cm.getCDMASubscription( obtainMessage(EVENT_POLL_STATE_CDMA_SUBSCRIPTION));
             pollState();
             // Signal strength polling stops when radio is off.
@@ -401,7 +346,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
                             networkId = Integer.parseInt(states[9]);
                         }
                     } catch (NumberFormatException ex) {
-                        Log.w(LOG_TAG, "error parsing cell location data: " + ex);
+                        loge("error parsing cell location data: " + ex);
                     }
                 }
 
@@ -428,56 +373,22 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
                 String cdmaSubscription[] = (String[])ar.result;
                 if (cdmaSubscription != null && cdmaSubscription.length >= 5) {
                     mMdn = cdmaSubscription[0];
-                    if (cdmaSubscription[1] != null) {
-                        String[] sid = cdmaSubscription[1].split(",");
-                        mHomeSystemId = new int[sid.length];
-                        for (int i = 0; i < sid.length; i++) {
-                            try {
-                                mHomeSystemId[i] = Integer.parseInt(sid[i]);
-                            } catch (NumberFormatException ex) {
-                                Log.e(LOG_TAG, "error parsing system id: ", ex);
-                            }
-                        }
-                    }
-                    Log.d(LOG_TAG,"GET_CDMA_SUBSCRIPTION SID=" + cdmaSubscription[1] );
-
-                    if (cdmaSubscription[2] != null) {
-                        String[] nid = cdmaSubscription[2].split(",");
-                        mHomeNetworkId = new int[nid.length];
-                        for (int i = 0; i < nid.length; i++) {
-                            try {
-                                mHomeNetworkId[i] = Integer.parseInt(nid[i]);
-                            } catch (NumberFormatException ex) {
-                                Log.e(LOG_TAG, "error parsing network id: ", ex);
-                            }
-                        }
-                    }
-                    Log.d(LOG_TAG,"GET_CDMA_SUBSCRIPTION NID=" + cdmaSubscription[2] );
+                    parseSidNid(cdmaSubscription[1], cdmaSubscription[2]);
+
                     mMin = cdmaSubscription[3];
                     mPrlVersion = cdmaSubscription[4];
-                    Log.d(LOG_TAG,"GET_CDMA_SUBSCRIPTION MDN=" + mMdn);
+                    if (DBG) log("GET_CDMA_SUBSCRIPTION: MDN=" + mMdn);
 
                     mIsMinInfoReady = true;
 
-                    int otaspMode = getOtasp();
-                    int oldOtaspMode = mCurrentOtaspMode;
-                    mCurrentOtaspMode = otaspMode;
-
-                    // Notify apps subscription info is ready
-                    if (cdmaForSubscriptionInfoReadyRegistrants != null) {
-                        Log.d(LOG_TAG, "call cdmaForSubscriptionInfoReady.notifyRegistrants()");
-                        cdmaForSubscriptionInfoReadyRegistrants.notifyRegistrants();
-                    }
-                    if (oldOtaspMode != mCurrentOtaspMode) {
-                        Log.d(LOG_TAG, "call phone.notifyOtaspChanged old otaspMode=" +
-                                oldOtaspMode + " new otaspMode=" + mCurrentOtaspMode);
-                        phone.notifyOtaspChanged(mCurrentOtaspMode);
-                    }
+                    updateOtaspState();
                     phone.getIccCard().broadcastIccStateChangedIntent(IccCard.INTENT_VALUE_ICC_IMSI,
                             null);
                 } else {
-                    Log.w(LOG_TAG,"error parsing cdmaSubscription params num="
+                    if (DBG) {
+                        log("GET_CDMA_SUBSCRIPTION: error parsing cdmaSubscription params num="
                             + cdmaSubscription.length);
+                    }
                 }
             }
             break;
@@ -532,26 +443,16 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
             if (ar.exception == null) {
                 ints = (int[]) ar.result;
                 int otaStatus = ints[0];
-                if (otaStatus == phone.CDMA_OTA_PROVISION_STATUS_COMMITTED
-                    || otaStatus == phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STOPPED) {
-                    Log.d(LOG_TAG, "Received OTA_PROGRAMMING Complete,Reload MDN ");
+                if (otaStatus == Phone.CDMA_OTA_PROVISION_STATUS_COMMITTED
+                    || otaStatus == Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STOPPED) {
+                    if (DBG) log("EVENT_OTA_PROVISION_STATUS_CHANGE: Complete, Reload MDN");
                     cm.getCDMASubscription( obtainMessage(EVENT_POLL_STATE_CDMA_SUBSCRIPTION));
                 }
             }
             break;
 
-        case EVENT_SET_RADIO_POWER_OFF:
-            synchronized(this) {
-                if (mPendingRadioPowerOffAfterDataOff) {
-                    if (DBG) log("EVENT_SET_RADIO_OFF, turn radio off now.");
-                    hangupAndPowerOff();
-                    mPendingRadioPowerOffAfterDataOff = false;
-                }
-            }
-            break;
-
         default:
-            Log.e(LOG_TAG, "Unhandled message with number: " + msg.what);
+            super.handleMessage(msg);
         break;
         }
     }
@@ -565,51 +466,14 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
             && cm.getRadioState() == CommandsInterface.RadioState.RADIO_OFF) {
             cm.setRadioPower(true, null);
         } else if (!mDesiredPowerState && cm.getRadioState().isOn()) {
-            DataConnectionTracker dcTracker = phone.mDataConnection;
-            if (! dcTracker.isDataConnectionAsDesired()) {
-                EventLog.writeEvent(EventLogTags.DATA_NETWORK_STATUS_ON_RADIO_OFF,
-                        dcTracker.getStateInString(),
-                        dcTracker.getAnyDataEnabled() ? 1 : 0);
-            }
+            DataConnectionTracker dcTracker = phone.mDataConnectionTracker;
 
             // If it's on and available and we want it off gracefully
-            powerOffRadioSafely();
+            powerOffRadioSafely(dcTracker);
         } // Otherwise, we're in the desired state
     }
 
     @Override
-    protected void powerOffRadioSafely() {
-        DataConnectionTracker dcTracker = phone.mDataConnection;
-
-        Message msg = dcTracker.obtainMessage(DataConnectionTracker.EVENT_CLEAN_UP_CONNECTION);
-        msg.obj = CDMAPhone.REASON_RADIO_TURNED_OFF;
-
-        synchronized (this) {
-            if (!mPendingRadioPowerOffAfterDataOff) {
-                DataConnectionTracker.State currentState = dcTracker.getState();
-                if (currentState != DataConnectionTracker.State.CONNECTED
-                    && currentState != DataConnectionTracker.State.DISCONNECTING
-                    && currentState != DataConnectionTracker.State.INITING) {
-                    msg.arg1 = 0; // tearDown is false as it is not needed.
-                    dcTracker.sendMessage(msg);
-                    if (DBG) log("Data disconnected, turn off radio right away.");
-                    hangupAndPowerOff();
-                } else {
-                    msg.arg1 = 1; // tearDown is true
-                    dcTracker.sendMessage(msg);
-                    if (sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF, 30000)) {
-                        if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio.");
-                        mPendingRadioPowerOffAfterDataOff = true;
-                    } else {
-                        Log.w(LOG_TAG, "Cannot send delayed Msg, turn off radio right away.");
-                        hangupAndPowerOff();
-                    }
-                }
-            }
-        }
-    }
-
-    @Override
     protected void updateSpnDisplay() {
         // TODO RUIM SPN is not implemented, EF_SPN has to be read and Display Condition
         //   Character Encoding, Language Indicator and SPN has to be set, something like below:
@@ -620,15 +484,16 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
 
         // mOperatorAlphaLong contains the ERI text
         String plmn = ss.getOperatorAlphaLong();
-        if (!TextUtils.equals(plmn, curPlmn)) {
+        if (!TextUtils.equals(plmn, mCurPlmn)) {
             // Allow A blank plmn, "" to set showPlmn to true. Previously, we
             // would set showPlmn to true only if plmn was not empty, i.e. was not
             // null and not blank. But this would cause us to incorrectly display
             // "No Service". Now showPlmn is set to true for any non null string.
             boolean showPlmn = plmn != null;
-            Log.d(LOG_TAG,
-                    String.format("updateSpnDisplay: changed sending intent" +
+            if (DBG) {
+                log(String.format("updateSpnDisplay: changed sending intent" +
                             " showPlmn='%b' plmn='%s'", showPlmn, plmn));
+            }
             Intent intent = new Intent(Intents.SPN_STRINGS_UPDATED_ACTION);
             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
             intent.putExtra(Intents.EXTRA_SHOW_SPN, false);
@@ -638,7 +503,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
             phone.getContext().sendStickyBroadcast(intent);
         }
 
-        curPlmn = plmn;
+        mCurPlmn = plmn;
     }
 
     @Override
@@ -647,14 +512,164 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
     }
 
     /**
-     * Handle the result of one of the pollState()-related requests
-     */
+    * Determine data network type based on radio technology.
+    */
+    protected void setCdmaTechnology(int radioTechnology){
+        mNewDataConnectionState = radioTechnologyToDataServiceState(radioTechnology);
+        newSS.setRadioTechnology(radioTechnology);
+        newNetworkType = radioTechnology;
+    }
 
-    @Override
-    protected void handlePollStateResult (int what, AsyncResult ar) {
+    /**
+    * Hanlde the PollStateResult message
+    */
+    protected void handlePollStateResultMessage(int what, AsyncResult ar){
         int ints[];
         String states[];
+        switch (what) {
+        case EVENT_POLL_STATE_REGISTRATION_CDMA: // Handle RIL_REQUEST_REGISTRATION_STATE.
+            states = (String[])ar.result;
+
+            int registrationState = 4;     //[0] registrationState
+            int radioTechnology = -1;      //[3] radioTechnology
+            int baseStationId = -1;        //[4] baseStationId
+            //[5] baseStationLatitude
+            int baseStationLatitude = CdmaCellLocation.INVALID_LAT_LONG;
+            //[6] baseStationLongitude
+            int baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
+            int cssIndicator = 0;          //[7] init with 0, because it is treated as a boolean
+            int systemId = 0;              //[8] systemId
+            int networkId = 0;             //[9] networkId
+            int roamingIndicator = -1;     //[10] Roaming indicator
+            int systemIsInPrl = 0;         //[11] Indicates if current system is in PRL
+            int defaultRoamingIndicator = 0;  //[12] Is default roaming indicator from PRL
+            int reasonForDenial = 0;       //[13] Denial reason if registrationState = 3
+
+            if (states.length == 14) {
+                try {
+                    if (states[0] != null) {
+                        registrationState = Integer.parseInt(states[0]);
+                    }
+                    if (states[3] != null) {
+                        radioTechnology = Integer.parseInt(states[3]);
+                    }
+                    if (states[4] != null) {
+                        baseStationId = Integer.parseInt(states[4]);
+                    }
+                    if (states[5] != null) {
+                        baseStationLatitude = Integer.parseInt(states[5]);
+                    }
+                    if (states[6] != null) {
+                        baseStationLongitude = Integer.parseInt(states[6]);
+                    }
+                    // Some carriers only return lat-lngs of 0,0
+                    if (baseStationLatitude == 0 && baseStationLongitude == 0) {
+                        baseStationLatitude  = CdmaCellLocation.INVALID_LAT_LONG;
+                        baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
+                    }
+                    if (states[7] != null) {
+                        cssIndicator = Integer.parseInt(states[7]);
+                    }
+                    if (states[8] != null) {
+                        systemId = Integer.parseInt(states[8]);
+                    }
+                    if (states[9] != null) {
+                        networkId = Integer.parseInt(states[9]);
+                    }
+                    if (states[10] != null) {
+                        roamingIndicator = Integer.parseInt(states[10]);
+                    }
+                    if (states[11] != null) {
+                        systemIsInPrl = Integer.parseInt(states[11]);
+                    }
+                    if (states[12] != null) {
+                        defaultRoamingIndicator = Integer.parseInt(states[12]);
+                    }
+                    if (states[13] != null) {
+                        reasonForDenial = Integer.parseInt(states[13]);
+                    }
+                } catch (NumberFormatException ex) {
+                    loge("EVENT_POLL_STATE_REGISTRATION_CDMA: error parsing: " + ex);
+                }
+            } else {
+                throw new RuntimeException("Warning! Wrong number of parameters returned from "
+                                     + "RIL_REQUEST_REGISTRATION_STATE: expected 14 got "
+                                     + states.length);
+            }
+
+            mRegistrationState = registrationState;
+            // When registration state is roaming and TSB58
+            // roaming indicator is not in the carrier-specified
+            // list of ERIs for home system, mCdmaRoaming is true.
+            mCdmaRoaming =
+                    regCodeIsRoaming(registrationState) && !isRoamIndForHomeSystem(states[10]);
+            newSS.setState (regCodeToServiceState(registrationState));
+
+            setCdmaTechnology(radioTechnology);
+
+            newSS.setCssIndicator(cssIndicator);
+            newSS.setSystemAndNetworkId(systemId, networkId);
+            mRoamingIndicator = roamingIndicator;
+            mIsInPrl = (systemIsInPrl == 0) ? false : true;
+            mDefaultRoamingIndicator = defaultRoamingIndicator;
+
 
+            // Values are -1 if not available.
+            newCellLoc.setCellLocationData(baseStationId, baseStationLatitude,
+                    baseStationLongitude, systemId, networkId);
+
+            if (reasonForDenial == 0) {
+                mRegistrationDeniedReason = ServiceStateTracker.REGISTRATION_DENIED_GEN;
+            } else if (reasonForDenial == 1) {
+                mRegistrationDeniedReason = ServiceStateTracker.REGISTRATION_DENIED_AUTH;
+            } else {
+                mRegistrationDeniedReason = "";
+            }
+
+            if (mRegistrationState == 3) {
+                if (DBG) log("Registration denied, " + mRegistrationDeniedReason);
+            }
+            break;
+
+        case EVENT_POLL_STATE_OPERATOR_CDMA: // Handle RIL_REQUEST_OPERATOR
+            String opNames[] = (String[])ar.result;
+
+            if (opNames != null && opNames.length >= 3) {
+                // If the NUMERIC field isn't valid use PROPERTY_CDMA_HOME_OPERATOR_NUMERIC
+                if ((opNames[2] == null) || (opNames[2].length() < 5)
+                        || ("00000".equals(opNames[2]))) {
+                    opNames[2] = SystemProperties.get(
+                            CDMAPhone.PROPERTY_CDMA_HOME_OPERATOR_NUMERIC, "00000");
+                    if (DBG) {
+                        log("RIL_REQUEST_OPERATOR.response[2], the numeric, " +
+                                " is bad. Using SystemProperties '" +
+                                        CDMAPhone.PROPERTY_CDMA_HOME_OPERATOR_NUMERIC +
+                                "'= " + opNames[2]);
+                    }
+                }
+                if (cm.getNvState().isNVReady()) {
+                    // In CDMA in case on NV, the ss.mOperatorAlphaLong is set later with the
+                    // ERI text, so here it is ignored what is coming from the modem.
+                    newSS.setOperatorName(null, opNames[1], opNames[2]);
+                } else {
+                    newSS.setOperatorName(opNames[0], opNames[1], opNames[2]);
+                }
+            } else {
+                if (DBG) log("EVENT_POLL_STATE_OPERATOR_CDMA: error parsing opNames");
+            }
+            break;
+        default:
+            loge("handlePollStateResultMessage: RIL response handle in wrong phone!"
+                    + " Expected CDMA RIL request and get GSM RIL request.");
+        break;
+        }
+    }
+
+    /**
+     * Handle the result of one of the pollState() - related requests
+     */
+    @Override
+    protected void handlePollStateResult(int what, AsyncResult ar) {
         // Ignore stale requests from last poll.
         if (ar.userObj != pollingContext) return;
 
@@ -677,158 +692,15 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
                 return;
             }
 
-            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",
-                        ar.exception);
+            if (err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW) {
+                loge("handlePollStateResult: RIL returned an error where it must succeed"
+                        + ar.exception);
             }
         } else try {
-            switch (what) {
-            case EVENT_POLL_STATE_REGISTRATION_CDMA: // Handle RIL_REQUEST_REGISTRATION_STATE.
-                states = (String[])ar.result;
-
-                int registrationState = 4;     //[0] registrationState
-                int radioTechnology = -1;      //[3] radioTechnology
-                int baseStationId = -1;        //[4] baseStationId
-                //[5] baseStationLatitude
-                int baseStationLatitude = CdmaCellLocation.INVALID_LAT_LONG;
-                //[6] baseStationLongitude
-                int baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
-                int cssIndicator = 0;          //[7] init with 0, because it is treated as a boolean
-                int systemId = 0;              //[8] systemId
-                int networkId = 0;             //[9] networkId
-                int roamingIndicator = -1;     //[10] Roaming indicator
-                int systemIsInPrl = 0;         //[11] Indicates if current system is in PRL
-                int defaultRoamingIndicator = 0;  //[12] Is default roaming indicator from PRL
-                int reasonForDenial = 0;       //[13] Denial reason if registrationState = 3
-
-                if (states.length == 14) {
-                    try {
-                        if (states[0] != null) {
-                            registrationState = Integer.parseInt(states[0]);
-                        }
-                        if (states[3] != null) {
-                            radioTechnology = Integer.parseInt(states[3]);
-                        }
-                        if (states[4] != null) {
-                            baseStationId = Integer.parseInt(states[4]);
-                        }
-                        if (states[5] != null) {
-                            baseStationLatitude = Integer.parseInt(states[5]);
-                        }
-                        if (states[6] != null) {
-                            baseStationLongitude = Integer.parseInt(states[6]);
-                        }
-                        // Some carriers only return lat-lngs of 0,0
-                        if (baseStationLatitude == 0 && baseStationLongitude == 0) {
-                            baseStationLatitude  = CdmaCellLocation.INVALID_LAT_LONG;
-                            baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
-                        }
-                        if (states[7] != null) {
-                            cssIndicator = Integer.parseInt(states[7]);
-                        }
-                        if (states[8] != null) {
-                            systemId = Integer.parseInt(states[8]);
-                        }
-                        if (states[9] != null) {
-                            networkId = Integer.parseInt(states[9]);
-                        }
-                        if (states[10] != null) {
-                            roamingIndicator = Integer.parseInt(states[10]);
-                        }
-                        if (states[11] != null) {
-                            systemIsInPrl = Integer.parseInt(states[11]);
-                        }
-                        if (states[12] != null) {
-                            defaultRoamingIndicator = Integer.parseInt(states[12]);
-                        }
-                        if (states[13] != null) {
-                            reasonForDenial = Integer.parseInt(states[13]);
-                        }
-                    } catch (NumberFormatException ex) {
-                        Log.w(LOG_TAG, "error parsing RegistrationState: " + ex);
-                    }
-                } else {
-                    throw new RuntimeException("Warning! Wrong number of parameters returned from "
-                                         + "RIL_REQUEST_REGISTRATION_STATE: expected 14 got "
-                                         + states.length);
-                }
-
-                mRegistrationState = registrationState;
-                // When registration state is roaming and TSB58
-                // roaming indicator is not in the carrier-specified
-                // list of ERIs for home system, mCdmaRoaming is true.
-                mCdmaRoaming =
-                        regCodeIsRoaming(registrationState) && !isRoamIndForHomeSystem(states[10]);
-                newSS.setState (regCodeToServiceState(registrationState));
-
-                this.newCdmaDataConnectionState =
-                        radioTechnologyToDataServiceState(radioTechnology);
-                newSS.setRadioTechnology(radioTechnology);
-                newNetworkType = radioTechnology;
-
-                newSS.setCssIndicator(cssIndicator);
-                newSS.setSystemAndNetworkId(systemId, networkId);
-                mRoamingIndicator = roamingIndicator;
-                mIsInPrl = (systemIsInPrl == 0) ? false : true;
-                mDefaultRoamingIndicator = defaultRoamingIndicator;
-
-
-                // Values are -1 if not available.
-                newCellLoc.setCellLocationData(baseStationId, baseStationLatitude,
-                        baseStationLongitude, systemId, networkId);
-
-                if (reasonForDenial == 0) {
-                    mRegistrationDeniedReason = ServiceStateTracker.REGISTRATION_DENIED_GEN;
-                } else if (reasonForDenial == 1) {
-                    mRegistrationDeniedReason = ServiceStateTracker.REGISTRATION_DENIED_AUTH;
-                } else {
-                    mRegistrationDeniedReason = "";
-                }
-
-                if (mRegistrationState == 3) {
-                    if (DBG) log("Registration denied, " + mRegistrationDeniedReason);
-                }
-                break;
-
-            case EVENT_POLL_STATE_OPERATOR_CDMA: // Handle RIL_REQUEST_OPERATOR
-                String opNames[] = (String[])ar.result;
-
-                if (opNames != null && opNames.length >= 3) {
-                    // If the NUMERIC field isn't valid use PROPERTY_CDMA_HOME_OPERATOR_NUMERIC
-                    if ((opNames[2] == null) || (opNames[2].length() < 5)
-                            || ("00000".equals(opNames[2]))) {
-                        opNames[2] = SystemProperties.get(
-                                CDMAPhone.PROPERTY_CDMA_HOME_OPERATOR_NUMERIC, "00000");
-                        if (DBG) {
-                            log("RIL_REQUEST_OPERATOR.response[2], the numeric, " +
-                                    " is bad. Using SystemProperties '" +
-                                            CDMAPhone.PROPERTY_CDMA_HOME_OPERATOR_NUMERIC +
-                                    "'= " + opNames[2]);
-                        }
-                    }
-                    if (cm.getRadioState().isNVReady()) {
-                        // In CDMA in case on NV, the ss.mOperatorAlphaLong is set later with the
-                        // ERI text, so here it is ignored what is coming from the modem.
-                        newSS.setOperatorName(null, opNames[1], opNames[2]);
-                    } else {
-                        newSS.setOperatorName(opNames[0], opNames[1], opNames[2]);
-                    }
-                } else {
-                    Log.w(LOG_TAG, "error parsing opNames");
-                }
-                break;
-
-            default:
-                Log.e(LOG_TAG, "RIL response handle in wrong phone!"
-                    + " Expected CDMA RIL request and get GSM RIL request.");
-            break;
-            }
-
+            handlePollStateResultMessage(what, ar);
         } catch (RuntimeException ex) {
-            Log.e(LOG_TAG, "Exception while polling service state. "
-                    + "Probably malformed RIL response.", ex);
+            loge("handlePollStateResult: Exception while polling service state. "
+                    + "Probably malformed RIL response." + ex);
         }
 
         pollingContext[0]--;
@@ -896,9 +768,8 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
 
     }
 
-    private void setSignalStrengthDefaultValues() {
-        mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1,
-                -1, -1, -1, -1, -1, false);
+    protected void setSignalStrengthDefaultValues() {
+        mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1, false);
     }
 
     /**
@@ -909,7 +780,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
      * and start over again if the radio notifies us that some
      * event has changed
      */
-    private void
+    protected void
     pollState() {
         pollingContext = new int[1];
         pollingContext[0] = 0;
@@ -936,7 +807,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
         case SIM_NOT_READY:
         case SIM_LOCKED_OR_ABSENT:
         case SIM_READY:
-            log("Radio Technology Change ongoing, setting SS to off");
+            if (DBG) log("Radio Technology Change ongoing, setting SS to off");
             newSS.setStateOff();
             newCellLoc.setStateInvalid();
             setSignalStrengthDefaultValues();
@@ -964,37 +835,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
         }
     }
 
-    private static String networkTypeToString(int type) {
-        String ret = "unknown";
-
-        switch (type) {
-        case DATA_ACCESS_CDMA_IS95A:
-        case DATA_ACCESS_CDMA_IS95B:
-            ret = "CDMA";
-            break;
-        case DATA_ACCESS_CDMA_1xRTT:
-            ret = "CDMA - 1xRTT";
-            break;
-        case DATA_ACCESS_CDMA_EvDo_0:
-            ret = "CDMA - EvDo rev. 0";
-            break;
-        case DATA_ACCESS_CDMA_EvDo_A:
-            ret = "CDMA - EvDo rev. A";
-            break;
-        case DATA_ACCESS_CDMA_EvDo_B:
-            ret = "CDMA - EvDo rev. B";
-            break;
-        default:
-            if (DBG) {
-                Log.e(LOG_TAG, "Wrong network. Can not return a string.");
-            }
-        break;
-        }
-
-        return ret;
-    }
-
-    private void fixTimeZone(String isoCountryCode) {
+    protected void fixTimeZone(String isoCountryCode) {
         TimeZone zone = null;
         // If the offset is (0, false) and the time zone property
         // is set, use the time zone property rather than GMT.
@@ -1031,8 +872,8 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
         }
     }
 
-    private void pollStateDone() {
-        if (DBG) log("Poll ServiceState done: oldSS=[" + ss + "] newSS=[" + newSS + "]");
+    protected void pollStateDone() {
+        if (DBG) log("pollStateDone: oldSS=[" + ss + "] newSS=[" + newSS + "]");
 
         boolean hasRegistered =
             ss.getState() != ServiceState.STATE_IN_SERVICE
@@ -1043,15 +884,15 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
             && newSS.getState() != ServiceState.STATE_IN_SERVICE;
 
         boolean hasCdmaDataConnectionAttached =
-            this.cdmaDataConnectionState != ServiceState.STATE_IN_SERVICE
-            && this.newCdmaDataConnectionState == ServiceState.STATE_IN_SERVICE;
+            mDataConnectionState != ServiceState.STATE_IN_SERVICE
+            && mNewDataConnectionState == ServiceState.STATE_IN_SERVICE;
 
         boolean hasCdmaDataConnectionDetached =
-            this.cdmaDataConnectionState == ServiceState.STATE_IN_SERVICE
-            && this.newCdmaDataConnectionState != ServiceState.STATE_IN_SERVICE;
+            mDataConnectionState == ServiceState.STATE_IN_SERVICE
+            && mNewDataConnectionState != ServiceState.STATE_IN_SERVICE;
 
         boolean hasCdmaDataConnectionChanged =
-                       cdmaDataConnectionState != newCdmaDataConnectionState;
+                       mDataConnectionState != mNewDataConnectionState;
 
         boolean hasNetworkTypeChanged = networkType != newNetworkType;
 
@@ -1065,10 +906,10 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
 
         // Add an event log when connection state changes
         if (ss.getState() != newSS.getState() ||
-                cdmaDataConnectionState != newCdmaDataConnectionState) {
+                mDataConnectionState != mNewDataConnectionState) {
             EventLog.writeEvent(EventLogTags.CDMA_SERVICE_STATE_CHANGE,
-                    ss.getState(), cdmaDataConnectionState,
-                    newSS.getState(), newCdmaDataConnectionState);
+                    ss.getState(), mDataConnectionState,
+                    newSS.getState(), mNewDataConnectionState);
         }
 
         ServiceState tss;
@@ -1082,7 +923,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
         cellLoc = newCellLoc;
         newCellLoc = tcl;
 
-        cdmaDataConnectionState = newCdmaDataConnectionState;
+        mDataConnectionState = mNewDataConnectionState;
         networkType = newNetworkType;
         // this new state has been applied - forget it until we get a new new state
         newNetworkType = 0;
@@ -1091,11 +932,11 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
 
         if (hasNetworkTypeChanged) {
             phone.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
-                    networkTypeToString(networkType));
+                    ServiceState.radioTechnologyToString(networkType));
         }
 
         if (hasRegistered) {
-            networkAttachedRegistrants.notifyRegistrants();
+            mNetworkAttachedRegistrants.notifyRegistrants();
         }
 
         if (hasChanged) {
@@ -1110,7 +951,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
                     eriText = phone.getContext().getText(
                             com.android.internal.R.string.roamingTextSearching).toString();
                 }
-                ss.setCdmaEriText(eriText);
+                ss.setOperatorAlphaLong(eriText);
             }
 
             String operatorNumeric;
@@ -1129,9 +970,9 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
                     isoCountryCode = MccTable.countryCodeForMcc(Integer.parseInt(
                             operatorNumeric.substring(0,3)));
                 } catch ( NumberFormatException ex){
-                    Log.w(LOG_TAG, "countryCodeForMcc error" + ex);
+                    loge("pollStateDone: countryCodeForMcc error" + ex);
                 } catch ( StringIndexOutOfBoundsException ex) {
-                    Log.w(LOG_TAG, "countryCodeForMcc error" + ex);
+                    loge("pollStateDone: countryCodeForMcc error" + ex);
                 }
 
                 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY,
@@ -1150,11 +991,11 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
         }
 
         if (hasCdmaDataConnectionAttached) {
-            cdmaDataConnectionAttachedRegistrants.notifyRegistrants();
+            mAttachedRegistrants.notifyRegistrants();
         }
 
         if (hasCdmaDataConnectionDetached) {
-            cdmaDataConnectionDetachedRegistrants.notifyRegistrants();
+            mDetachedRegistrants.notifyRegistrants();
         }
 
         if (hasCdmaDataConnectionChanged || hasNetworkTypeChanged) {
@@ -1162,11 +1003,11 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
         }
 
         if (hasRoamingOn) {
-            roamingOnRegistrants.notifyRegistrants();
+            mRoamingOnRegistrants.notifyRegistrants();
         }
 
         if (hasRoamingOff) {
-            roamingOffRegistrants.notifyRegistrants();
+            mRoamingOffRegistrants.notifyRegistrants();
         }
 
         if (hasLocationChanged) {
@@ -1213,7 +1054,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
      * This code should probably be hoisted to the base class so
      * the fix, when added, works for both.
      */
-    private void
+    protected void
     queueNextSignalStrengthPoll() {
         if (dontPollSignalStrength || (cm.getRadioState().isGsm())) {
             // The radio is telling us about signal strength changes
@@ -1234,7 +1075,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
      *  send signal-strength-changed notification if changed
      *  Called both for solicited and unsolicited signal strength updates
      */
-    private void
+    protected void
     onSignalStrengthResult(AsyncResult ar) {
         SignalStrength oldSignalStrength = mSignalStrength;
 
@@ -1253,19 +1094,19 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
             //log(String.format("onSignalStrengthResult cdmaDbm=%d cdmaEcio=%d evdoRssi=%d evdoEcio=%d evdoSnr=%d",
             //        cdmaDbm, cdmaEcio, evdoRssi, evdoEcio, evdoSnr));
             mSignalStrength = new SignalStrength(99, -1, cdmaDbm, cdmaEcio,
-                    evdoRssi, evdoEcio, evdoSnr, -1, -1, -1, -1, -1, false);
+                    evdoRssi, evdoEcio, evdoSnr, false);
         }
 
         try {
             phone.notifySignalStrength();
         } catch (NullPointerException ex) {
-            log("onSignalStrengthResult() Phone already destroyed: " + ex
+            loge("onSignalStrengthResult() Phone already destroyed: " + ex
                     + "SignalStrength not notified");
         }
     }
 
 
-    private int radioTechnologyToDataServiceState(int code) {
+    protected int radioTechnologyToDataServiceState(int code) {
         int retVal = ServiceState.STATE_OUT_OF_SERVICE;
         switch(code) {
         case 0:
@@ -1279,17 +1120,18 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
         case 7: // RADIO_TECHNOLOGY_EVDO_0
         case 8: // RADIO_TECHNOLOGY_EVDO_A
         case 12: // RADIO_TECHNOLOGY_EVDO_B
+        case 13: // RADIO_TECHNOLOGY_EHRPD
             retVal = ServiceState.STATE_IN_SERVICE;
             break;
         default:
-            Log.e(LOG_TAG, "Wrong radioTechnology code.");
+            loge("radioTechnologyToDataServiceState: Wrong radioTechnology code.");
         break;
         }
         return(retVal);
     }
 
     /** code is registration state 0-5 from TS 27.007 7.2 */
-    private int
+    protected int
     regCodeToServiceState(int code) {
         switch (code) {
         case 0: // Not searching and not registered
@@ -1304,18 +1146,13 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
             return ServiceState.STATE_IN_SERVICE;
 
         default:
-            Log.w(LOG_TAG, "unexpected service state " + code);
+            loge("regCodeToServiceState: unexpected service state " + code);
         return ServiceState.STATE_OUT_OF_SERVICE;
         }
     }
 
-    /**
-     * @return The current CDMA data connection state. ServiceState.RADIO_TECHNOLOGY_1xRTT or
-     * ServiceState.RADIO_TECHNOLOGY_EVDO is the same as "attached" and
-     * ServiceState.RADIO_TECHNOLOGY_UNKNOWN is the same as detached.
-     */
-    /*package*/ int getCurrentCdmaDataConnectionState() {
-        return cdmaDataConnectionState;
+    public int getCurrentDataConnectionState() {
+        return mDataConnectionState;
     }
 
     /**
@@ -1388,8 +1225,10 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
         // tz is in number of quarter-hours
 
         long start = SystemClock.elapsedRealtime();
-        Log.i(LOG_TAG, "NITZ: " + nitz + "," + nitzReceiveTime +
+        if (DBG) {
+            log("NITZ: " + nitz + "," + nitzReceiveTime +
                         " start=" + start + " delay=" + (start - nitzReceiveTime));
+        }
 
         try {
             /* NITZ time (hour:min:sec) will be in UTC but it supplies the timezone
@@ -1486,7 +1325,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
 
             String ignore = SystemProperties.get("gsm.ignore-nitz");
             if (ignore != null && ignore.equals("yes")) {
-                Log.i(LOG_TAG, "NITZ: Not setting clock because gsm.ignore-nitz is set");
+                if (DBG) log("NITZ: Not setting clock because gsm.ignore-nitz is set");
                 return;
             }
 
@@ -1501,17 +1340,21 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
 
                 if (millisSinceNitzReceived < 0) {
                     // Sanity check: something is wrong
-                    Log.i(LOG_TAG, "NITZ: not setting time, clock has rolled "
+                    if (DBG) {
+                        log("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 "
+                    if (DBG) {
+                        log("NITZ: not setting time, processing has taken "
                                     + (millisSinceNitzReceived / (1000 * 60 * 60 * 24))
                                     + " days");
+                    }
                     return;
                 }
 
@@ -1531,14 +1374,18 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
 
                     if ((mSavedAtTime == 0) || (timeSinceLastUpdate > nitzUpdateSpacing)
                             || (Math.abs(gained) > nitzUpdateDiff)) {
-                        Log.i(LOG_TAG, "NITZ: Auto updating time of day to " + c.getTime()
+                        if (DBG) {
+                            log("NITZ: Auto updating time of day to " + c.getTime()
                                 + " NITZ receive delay=" + millisSinceNitzReceived
                                 + "ms gained=" + gained + "ms from " + nitz);
+                        }
 
                         setAndBroadcastNetworkSetTime(c.getTimeInMillis());
                     } else {
-                        Log.i(LOG_TAG, "NITZ: ignore, a previous update was "
+                        if (DBG) {
+                            log("NITZ: ignore, a previous update was "
                                 + timeSinceLastUpdate + "ms ago and gained=" + gained + "ms");
+                        }
                         return;
                     }
                 }
@@ -1546,17 +1393,17 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
                 /**
                  * Update properties and save the time we did the update
                  */
-                Log.i(LOG_TAG, "NITZ: update nitz time property");
+                if (DBG) log("NITZ: update nitz time property");
                 SystemProperties.set("gsm.nitz.time", String.valueOf(c.getTimeInMillis()));
                 mSavedTime = c.getTimeInMillis();
                 mSavedAtTime = SystemClock.elapsedRealtime();
             } finally {
                 long end = SystemClock.elapsedRealtime();
-                Log.i(LOG_TAG, "NITZ: end=" + end + " dur=" + (end - start));
+                if (DBG) log("NITZ: end=" + end + " dur=" + (end - start));
                 mWakeLock.release();
             }
         } catch (RuntimeException ex) {
-            Log.e(LOG_TAG, "NITZ: Parsing NITZ time " + nitz, ex);
+            loge("NITZ: Parsing NITZ time " + nitz + " ex=" + ex);
         }
     }
 
@@ -1614,8 +1461,9 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
         if (Settings.System.getInt(cr, Settings.System.AUTO_TIME, 0) == 0) {
             return;
         }
-        Log.d(LOG_TAG, "Reverting to NITZ Time: mSavedTime=" + mSavedTime
-                + " mSavedAtTime=" + mSavedAtTime);
+        if (DBG) {
+            log("revertToNitzTime: mSavedTime=" + mSavedTime + " mSavedAtTime=" + mSavedAtTime);
+        }
         if (mSavedTime != 0 && mSavedAtTime != 0) {
             setAndBroadcastNetworkSetTime(mSavedTime
                     + (SystemClock.elapsedRealtime() - mSavedAtTime));
@@ -1627,7 +1475,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
                 Settings.System.AUTO_TIME_ZONE, 0) == 0) {
             return;
         }
-        Log.d(LOG_TAG, "Reverting to NITZ TimeZone: tz='" + mSavedTimeZone);
+        if (DBG) log("revertToNitzTimeZone: tz='" + mSavedTimeZone);
         if (mSavedTimeZone != null) {
             setAndBroadcastNetworkSetTimeZone(mSavedTimeZone);
         }
@@ -1662,7 +1510,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
      * @return true if phone is camping on a technology
      * that could support voice and data simultaneously.
      */
-    boolean isConcurrentVoiceAndData() {
+    public boolean isConcurrentVoiceAndDataAllowed() {
         // Note: it needs to be confirmed which CDMA network types
         // can support voice and data calls concurrently.
         // For the time-being, the return value will be false.
@@ -1707,30 +1555,12 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
     }
 
     /**
-     * process the pending request to turn radio off after data is disconnected
-     *
-     * return true if there is pending request to process; false otherwise.
-     */
-    public boolean processPendingRadioPowerOffAfterDataOff() {
-        synchronized(this) {
-            if (mPendingRadioPowerOffAfterDataOff) {
-                if (DBG) log("Process pending request to turn radio off.");
-                removeMessages(EVENT_SET_RADIO_POWER_OFF);
-                hangupAndPowerOff();
-                mPendingRadioPowerOffAfterDataOff = false;
-                return true;
-            }
-            return false;
-        }
-    }
-
-    /**
      * Returns OTASP_UNKNOWN, OTASP_NEEDED or OTASP_NOT_NEEDED
      */
     int getOtasp() {
         int provisioningState;
         if (mMin == null || (mMin.length() < 6)) {
-            if (DBG) Log.d(LOG_TAG, "getOtasp: bad mMin='" + mMin + "'");
+            if (DBG) log("getOtasp: bad mMin='" + mMin + "'");
             provisioningState = OTASP_UNKNOWN;
         } else {
             if ((mMin.equals(UNACTIVATED_MIN_VALUE)
@@ -1741,20 +1571,73 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
                 provisioningState = OTASP_NOT_NEEDED;
             }
         }
-        if (DBG) Log.d(LOG_TAG, "getOtasp: state=" + provisioningState);
+        if (DBG) log("getOtasp: state=" + provisioningState);
         return provisioningState;
     }
 
     @Override
-    protected void log(String s) {
-        Log.d(LOG_TAG, "[CdmaServiceStateTracker] " + s);
-    }
-
-    private void hangupAndPowerOff() {
+    protected void hangupAndPowerOff() {
         // hang up all active voice calls
         phone.mCT.ringingCall.hangupIfAlive();
         phone.mCT.backgroundCall.hangupIfAlive();
         phone.mCT.foregroundCall.hangupIfAlive();
         cm.setRadioPower(false, null);
     }
+
+    protected void parseSidNid (String sidStr, String nidStr) {
+        if (sidStr != null) {
+            String[] sid = sidStr.split(",");
+            mHomeSystemId = new int[sid.length];
+            for (int i = 0; i < sid.length; i++) {
+                try {
+                    mHomeSystemId[i] = Integer.parseInt(sid[i]);
+                } catch (NumberFormatException ex) {
+                    loge("error parsing system id: " + ex);
+                }
+            }
+        }
+        if (DBG) log("CDMA_SUBSCRIPTION: SID=" + sidStr);
+
+        if (nidStr != null) {
+            String[] nid = nidStr.split(",");
+            mHomeNetworkId = new int[nid.length];
+            for (int i = 0; i < nid.length; i++) {
+                try {
+                    mHomeNetworkId[i] = Integer.parseInt(nid[i]);
+                } catch (NumberFormatException ex) {
+                    loge("CDMA_SUBSCRIPTION: error parsing network id: " + ex);
+                }
+            }
+        }
+        if (DBG) log("CDMA_SUBSCRIPTION: NID=" + nidStr);
+    }
+
+    protected void updateOtaspState() {
+        int otaspMode = getOtasp();
+        int oldOtaspMode = mCurrentOtaspMode;
+        mCurrentOtaspMode = otaspMode;
+
+        // Notify apps subscription info is ready
+        if (cdmaForSubscriptionInfoReadyRegistrants != null) {
+            if (DBG) log("CDMA_SUBSCRIPTION: call notifyRegistrants()");
+            cdmaForSubscriptionInfoReadyRegistrants.notifyRegistrants();
+        }
+        if (oldOtaspMode != mCurrentOtaspMode) {
+            if (DBG) {
+                log("CDMA_SUBSCRIPTION: call notifyOtaspChanged old otaspMode=" +
+                    oldOtaspMode + " new otaspMode=" + mCurrentOtaspMode);
+            }
+            phone.notifyOtaspChanged(mCurrentOtaspMode);
+        }
+    }
+
+    @Override
+    protected void log(String s) {
+        Log.d(LOG_TAG, "[CdmaSST] " + s);
+    }
+
+    @Override
+    protected void loge(String s) {
+        Log.e(LOG_TAG, "[CdmaSST] " + s);
+    }
 }
index 734badd..11f44d4 100644 (file)
@@ -25,8 +25,8 @@ import com.android.internal.telephony.IccCard;
  */
 public final class RuimCard extends IccCard {
 
-    RuimCard(CDMAPhone phone) {
-        super(phone, "CDMA", true);
+    RuimCard(CDMAPhone phone, String LOG_TAG, boolean dbg) {
+        super(phone, LOG_TAG, dbg);
         mPhone.mCM.registerForRUIMLockedOrAbsent(mHandler, EVENT_ICC_LOCKED_OR_ABSENT, null);
         mPhone.mCM.registerForOffOrNotAvailable(mHandler, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
         mPhone.mCM.registerForRUIMReady(mHandler, EVENT_ICC_READY, null);
@@ -35,6 +35,7 @@ public final class RuimCard extends IccCard {
 
     @Override
     public void dispose() {
+        super.dispose();
         //Unregister for all events
         mPhone.mCM.unregisterForRUIMLockedOrAbsent(mHandler);
         mPhone.mCM.unregisterForOffOrNotAvailable(mHandler);
@@ -43,7 +44,7 @@ public final class RuimCard extends IccCard {
 
     @Override
     public String getServiceProviderName () {
-        return ((CDMAPhone)mPhone).mRuimRecords.getServiceProviderName();
+        return mPhone.mIccRecords.getServiceProviderName();
     }
  }
 
index 6e12f24..ce33066 100644 (file)
@@ -32,7 +32,7 @@ public class RuimPhoneBookInterfaceManager extends IccPhoneBookInterfaceManager
 
     public RuimPhoneBookInterfaceManager(CDMAPhone phone) {
         super(phone);
-        adnCache = phone.mRuimRecords.getAdnCache();
+        adnCache = phone.mIccRecords.getAdnCache();
         //NOTE service "simphonebook" added by IccSmsInterfaceManagerProxy
     }
 
index 87b0c60..719eff3 100644 (file)
@@ -87,18 +87,19 @@ public final class RuimRecords extends IccRecords {
         p.mCM.registerForRUIMReady(this, EVENT_RUIM_READY, null);
         p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
         // NOTE the EVENT_SMS_ON_RUIM is not registered
-        p.mCM.setOnIccRefresh(this, EVENT_RUIM_REFRESH, null);
+        p.mCM.registerForIccRefresh(this, EVENT_RUIM_REFRESH, null);
 
         // Start off by setting empty state
         onRadioOffOrNotAvailable();
 
     }
 
+    @Override
     public void dispose() {
         //Unregister for all events
         phone.mCM.unregisterForRUIMReady(this);
         phone.mCM.unregisterForOffOrNotAvailable( this);
-        phone.mCM.unSetOnIccRefresh(this);
+        phone.mCM.unregisterForIccRefresh(this);
     }
 
     @Override
@@ -293,7 +294,7 @@ public final class RuimRecords extends IccRecords {
 
         recordsLoadedRegistrants.notifyRegistrants(
             new AsyncResult(null, null, null));
-        ((CDMAPhone) phone).mRuimCard.broadcastIccStateChangedIntent(
+        phone.mIccCard.broadcastIccStateChangedIntent(
                 RuimCard.INTENT_VALUE_ICC_LOADED, null);
     }
 
@@ -302,7 +303,7 @@ public final class RuimRecords extends IccRecords {
           READY is sent before IMSI ready
         */
 
-        ((CDMAPhone) phone).mRuimCard.broadcastIccStateChangedIntent(
+        phone.mIccCard.broadcastIccStateChangedIntent(
                 RuimCard.INTENT_VALUE_ICC_READY, null);
 
         fetchRuimRecords();
@@ -324,8 +325,13 @@ public final class RuimRecords extends IccRecords {
         // Further records that can be inserted are Operator/OEM dependent
     }
 
+    /**
+     * {@inheritDoc}
+     *
+     * No Display rule for RUIMs yet.
+     */
     @Override
-    protected int getDisplayRule(String plmn) {
+    public int getDisplayRule(String plmn) {
         // TODO together with spn
         return 0;
     }
old mode 100644 (file)
new mode 100755 (executable)
index cf06dab..7a45e15
@@ -34,6 +34,9 @@ import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
 import com.android.internal.util.BitwiseInputStream;
 import com.android.internal.util.BitwiseOutputStream;
 
+import android.content.res.Resources;
+
+
 
 /**
  * An object to encode and decode CDMA SMS bearer data.
@@ -582,7 +585,6 @@ public final class BearerData {
                     uData.payload = new byte[0];
                     uData.numFields = 0;
                 } else {
-                    uData.payload = uData.payload;
                     uData.numFields = uData.payload.length;
                 }
             } else {
@@ -910,6 +912,16 @@ public final class BearerData {
         return true;
     }
 
+    private static String decodeUtf8(byte[] data, int offset, int numFields)
+        throws CodingException
+    {
+        try {
+            return new String(data, offset, numFields, "UTF-8");
+        } catch (java.io.UnsupportedEncodingException ex) {
+            throw new CodingException("UTF-8 decode failed: " + ex);
+        }
+    }
+
     private static String decodeUtf16(byte[] data, int offset, int numFields)
         throws CodingException
     {
@@ -994,9 +1006,15 @@ public final class BearerData {
         }
         switch (userData.msgEncoding) {
         case UserData.ENCODING_OCTET:
+            /*
+            *  Octet decoding depends on the carrier service.
+            */
+            boolean decodingtypeUTF8 = Resources.getSystem()
+                    .getBoolean(com.android.internal.R.bool.config_sms_utf8_support);
+
             // Strip off any padding bytes, meaning any differences between the length of the
-            // array and the target length specified by numFields.  This is to avoid any confusion
-            // by code elsewhere that only considers the payload array length.
+            // array and the target length specified by numFields.  This is to avoid any
+            // confusion by code elsewhere that only considers the payload array length.
             byte[] payload = new byte[userData.numFields];
             int copyLen = userData.numFields < userData.payload.length
                     ? userData.numFields : userData.payload.length;
@@ -1004,9 +1022,13 @@ public final class BearerData {
             System.arraycopy(userData.payload, 0, payload, 0, copyLen);
             userData.payload = payload;
 
-            // There are many devices in the market that send 8bit text sms (latin encoded) as
-            // octet encoded.
-            userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields);
+            if (!decodingtypeUTF8) {
+                // There are many devices in the market that send 8bit text sms (latin encoded) as
+                // octet encoded.
+                userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields);
+            } else {
+                userData.payloadStr = decodeUtf8(userData.payload, offset, userData.numFields);
+            }
             break;
         case UserData.ENCODING_IA5:
         case UserData.ENCODING_7BIT_ASCII:
index c17197e..275c8fe 100644 (file)
@@ -33,6 +33,7 @@ import android.telephony.CellLocation;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
+import com.android.internal.telephony.CallTracker;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -72,6 +73,7 @@ import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.telephony.UUSInfo;
 import com.android.internal.telephony.test.SimulatedRadioControl;
 import com.android.internal.telephony.IccVmNotSupportedException;
+import com.android.internal.telephony.ServiceStateTracker;
 
 import java.io.IOException;
 import java.net.InetSocketAddress;
@@ -100,9 +102,6 @@ public class GSMPhone extends PhoneBase {
     // Instance Variables
     GsmCallTracker mCT;
     GsmServiceStateTracker mSST;
-    GsmSMSDispatcher mSMS;
-    SIMRecords mSIMRecords;
-    SimCard mSimCard;
     CatService mStkService;
     ArrayList <GsmMmiCode> mPendingMMIs = new ArrayList<GsmMmiCode>();
     SimPhoneBookInterfaceManager mSimPhoneBookIntManager;
@@ -143,24 +142,23 @@ public class GSMPhone extends PhoneBase {
         mSST = new GsmServiceStateTracker (this);
         mSMS = new GsmSMSDispatcher(this);
         mIccFileHandler = new SIMFileHandler(this);
-        mSIMRecords = new SIMRecords(this);
-        mDataConnection = new GsmDataConnectionTracker (this);
-        mSimCard = new SimCard(this);
+        mIccRecords = new SIMRecords(this);
+        mDataConnectionTracker = new GsmDataConnectionTracker (this);
+        mIccCard = new SimCard(this);
         if (!unitTestMode) {
             mSimPhoneBookIntManager = new SimPhoneBookInterfaceManager(this);
             mSimSmsIntManager = new SimSmsInterfaceManager(this, mSMS);
             mSubInfo = new PhoneSubInfo(this);
         }
-        mStkService = CatService.getInstance(mCM, mSIMRecords, mContext,
-                (SIMFileHandler)mIccFileHandler, mSimCard);
+        mStkService = CatService.getInstance(mCM, mIccRecords, mContext, mIccFileHandler, mIccCard);
 
         mCM.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
-        mSIMRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
+        mIccRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
         mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
         mCM.registerForOn(this, EVENT_RADIO_ON, null);
         mCM.setOnUSSD(this, EVENT_USSD, null);
         mCM.setOnSuppServiceNotification(this, EVENT_SSN, null);
-        mSST.registerForNetworkAttach(this, EVENT_REGISTERED_TO_NETWORK, null);
+        mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null);
 
         if (false) {
             try {
@@ -207,10 +205,10 @@ public class GSMPhone extends PhoneBase {
 
             //Unregister from all former registered events
             mCM.unregisterForAvailable(this); //EVENT_RADIO_AVAILABLE
-            mSIMRecords.unregisterForRecordsLoaded(this); //EVENT_SIM_RECORDS_LOADED
+            mIccRecords.unregisterForRecordsLoaded(this); //EVENT_SIM_RECORDS_LOADED
             mCM.unregisterForOffOrNotAvailable(this); //EVENT_RADIO_OFF_OR_NOT_AVAILABLE
             mCM.unregisterForOn(this); //EVENT_RADIO_ON
-            mSST.unregisterForNetworkAttach(this); //EVENT_REGISTERED_TO_NETWORK
+            mSST.unregisterForNetworkAttached(this); //EVENT_REGISTERED_TO_NETWORK
             mCM.unSetOnUSSD(this);
             mCM.unSetOnSuppServiceNotification(this);
 
@@ -219,11 +217,11 @@ public class GSMPhone extends PhoneBase {
             //Force all referenced classes to unregister their former registered events
             mStkService.dispose();
             mCT.dispose();
-            mDataConnection.dispose();
+            mDataConnectionTracker.dispose();
             mSST.dispose();
             mIccFileHandler.dispose(); // instance of SimFileHandler
-            mSIMRecords.dispose();
-            mSimCard.dispose();
+            mIccRecords.dispose();
+            mIccCard.dispose();
             mSimPhoneBookIntManager.dispose();
             mSimSmsIntManager.dispose();
             mSubInfo.dispose();
@@ -237,10 +235,10 @@ public class GSMPhone extends PhoneBase {
             this.mSimSmsIntManager = null;
             this.mSMS = null;
             this.mSubInfo = null;
-            this.mSIMRecords = null;
+            this.mIccRecords = null;
             this.mIccFileHandler = null;
-            this.mSimCard = null;
-            this.mDataConnection = null;
+            this.mIccCard = null;
+            this.mDataConnectionTracker = null;
             this.mCT = null;
             this.mSST = null;
     }
@@ -275,12 +273,12 @@ public class GSMPhone extends PhoneBase {
         return mSST.mSignalStrength;
     }
 
-    public boolean getMessageWaitingIndicator() {
-        return mSIMRecords.getVoiceMessageWaiting();
+    public CallTracker getCallTracker() {
+        return mCT;
     }
 
-    public boolean getCallForwardingIndicator() {
-        return mSIMRecords.getVoiceCallForwardingFlag();
+    public ServiceStateTracker getServiceStateTracker() {
+        return mSST;
     }
 
     public List<? extends MmiCode>
@@ -301,11 +299,14 @@ public class GSMPhone extends PhoneBase {
             // If we're out of service, open TCP sockets may still work
             // but no data will flow
             ret = DataState.DISCONNECTED;
-        } else if (mDataConnection.isApnTypeEnabled(apnType) == false ||
-                mDataConnection.isApnTypeActive(apnType) == false) {
+        } else if (mDataConnectionTracker.isApnTypeEnabled(apnType) == false ||
+                mDataConnectionTracker.isApnTypeActive(apnType) == false) {
+            //TODO: isApnTypeActive() is just checking whether ApnContext holds
+            //      Dataconnection or not. Checking each ApnState below should
+            //      provide the same state. Calling isApnTypeActive() can be removed.
             ret = DataState.DISCONNECTED;
         } else { /* mSST.gprsState == ServiceState.STATE_IN_SERVICE */
-            switch (mDataConnection.getState()) {
+            switch (mDataConnectionTracker.getState(apnType)) {
                 case FAILED:
                 case IDLE:
                     ret = DataState.DISCONNECTED;
@@ -314,7 +315,7 @@ public class GSMPhone extends PhoneBase {
                 case CONNECTED:
                 case DISCONNECTING:
                     if ( mCT.state != Phone.State.IDLE
-                            && !mSST.isConcurrentVoiceAndData()) {
+                            && !mSST.isConcurrentVoiceAndDataAllowed()) {
                         ret = DataState.SUSPENDED;
                     } else {
                         ret = DataState.CONNECTED;
@@ -336,7 +337,7 @@ public class GSMPhone extends PhoneBase {
         DataActivityState ret = DataActivityState.NONE;
 
         if (mSST.getCurrentGprsState() == ServiceState.STATE_IN_SERVICE) {
-            switch (mDataConnection.getActivity()) {
+            switch (mDataConnectionTracker.getActivity()) {
                 case DATAIN:
                     ret = DataActivityState.DATAIN;
                 break;
@@ -404,7 +405,7 @@ public class GSMPhone extends PhoneBase {
         mNotifier.notifySignalStrength(this);
     }
 
-    /*package*/ void
+    public void
     notifyDataConnectionFailed(String reason, String apnType) {
         mNotifier.notifyDataConnectionFailed(this, reason, apnType);
     }
@@ -412,7 +413,7 @@ public class GSMPhone extends PhoneBase {
     /*package*/ void
     updateMessageWaitingIndicator(boolean mwi) {
         // this also calls notifyMessageWaitingIndicator()
-        mSIMRecords.setVoiceMessageWaiting(1, mwi ? -1 : 0);
+        mIccRecords.setVoiceMessageWaiting(1, mwi ? -1 : 0);
     }
 
     public void
@@ -813,7 +814,7 @@ public class GSMPhone extends PhoneBase {
 
     public String getVoiceMailNumber() {
         // Read from the SIM. If its null, try reading from the shared preference area.
-        String number = mSIMRecords.getVoiceMailNumber();
+        String number = mIccRecords.getVoiceMailNumber();
         if (TextUtils.isEmpty(number)) {
             SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
             number = sp.getString(VM_NUMBER, null);
@@ -836,7 +837,7 @@ public class GSMPhone extends PhoneBase {
     public String getVoiceMailAlphaTag() {
         String ret;
 
-        ret = mSIMRecords.getVoiceMailAlphaTag();
+        ret = mIccRecords.getVoiceMailAlphaTag();
 
         if (ret == null || ret.length() == 0) {
             return mContext.getText(
@@ -865,23 +866,19 @@ public class GSMPhone extends PhoneBase {
     }
 
     public String getSubscriberId() {
-        return mSIMRecords.imsi;
-    }
-
-    public String getIccSerialNumber() {
-        return mSIMRecords.iccid;
+        return mIccRecords.getIMSI();
     }
 
     public String getLine1Number() {
-        return mSIMRecords.getMsisdnNumber();
+        return mIccRecords.getMsisdnNumber();
     }
 
     public String getLine1AlphaTag() {
-        return mSIMRecords.getMsisdnAlphaTag();
+        return mIccRecords.getMsisdnAlphaTag();
     }
 
     public void setLine1Number(String alphaTag, String number, Message onComplete) {
-        mSIMRecords.setMsisdnNumber(alphaTag, number, onComplete);
+        mIccRecords.setMsisdnNumber(alphaTag, number, onComplete);
     }
 
     public void setVoiceMailNumber(String alphaTag,
@@ -891,7 +888,7 @@ public class GSMPhone extends PhoneBase {
         Message resp;
         mVmNumber = voiceMailNumber;
         resp = obtainMessage(EVENT_SET_VM_NUMBER_DONE, 0, 0, onComplete);
-        mSIMRecords.setVoiceMailNumber(alphaTag, mVmNumber, resp);
+        mIccRecords.setVoiceMailNumber(alphaTag, mVmNumber, resp);
     }
 
     private boolean isValidCommandInterfaceCFReason (int commandInterfaceCFReason) {
@@ -981,15 +978,6 @@ public class GSMPhone extends PhoneBase {
         mCM.setCallWaiting(enable, CommandsInterface.SERVICE_CLASS_VOICE, onComplete);
     }
 
-    public boolean
-    getIccRecordsLoaded() {
-        return mSIMRecords.getRecordsLoaded();
-    }
-
-    public IccCard getIccCard() {
-        return mSimCard;
-    }
-
     public void
     getAvailableNetworks(Message response) {
         mCM.getAvailableNetworks(response);
@@ -1062,10 +1050,6 @@ public class GSMPhone extends PhoneBase {
         mCM.getDataCallList(response);
     }
 
-    public List<DataConnection> getCurrentDataConnectionList () {
-        return mDataConnection.getAllDataConnections();
-    }
-
     public void updateServiceLocation() {
         mSST.enableSingleLocationUpdate();
     }
@@ -1079,11 +1063,11 @@ public class GSMPhone extends PhoneBase {
     }
 
     public boolean getDataRoamingEnabled() {
-        return mDataConnection.getDataOnRoamingEnabled();
+        return mDataConnectionTracker.getDataOnRoamingEnabled();
     }
 
     public void setDataRoamingEnabled(boolean enable) {
-        mDataConnection.setDataOnRoamingEnabled(enable);
+        mDataConnectionTracker.setDataOnRoamingEnabled(enable);
     }
 
     /**
@@ -1273,7 +1257,7 @@ public class GSMPhone extends PhoneBase {
             case EVENT_SET_CALL_FORWARD_DONE:
                 ar = (AsyncResult)msg.obj;
                 if (ar.exception == null) {
-                    mSIMRecords.setVoiceCallForwardingFlag(1, msg.arg1 == 1);
+                    mIccRecords.setVoiceCallForwardingFlag(1, msg.arg1 == 1);
                 }
                 onComplete = (Message) ar.userObj;
                 if (onComplete != null) {
@@ -1337,11 +1321,11 @@ public class GSMPhone extends PhoneBase {
      * @return true for success; false otherwise.
      */
     boolean updateCurrentCarrierInProvider() {
-        if (mSIMRecords != null) {
+        if (mIccRecords != null) {
             try {
                 Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current");
                 ContentValues map = new ContentValues();
-                map.put(Telephony.Carriers.NUMERIC, mSIMRecords.getSIMOperatorNumeric());
+                map.put(Telephony.Carriers.NUMERIC, mIccRecords.getOperatorNumeric());
                 mContext.getContentResolver().insert(uri, map);
                 return true;
             } catch (SQLException e) {
@@ -1406,11 +1390,11 @@ public class GSMPhone extends PhoneBase {
         if (infos == null || infos.length == 0) {
             // Assume the default is not active
             // Set unconditional CFF in SIM to false
-            mSIMRecords.setVoiceCallForwardingFlag(1, false);
+            mIccRecords.setVoiceCallForwardingFlag(1, false);
         } else {
             for (int i = 0, s = infos.length; i < s; i++) {
                 if ((infos[i].serviceClass & SERVICE_CLASS_VOICE) != 0) {
-                    mSIMRecords.setVoiceCallForwardingFlag(1, (infos[i].status == 1));
+                    mIccRecords.setVoiceCallForwardingFlag(1, (infos[i].status == 1));
                     // should only have the one
                     break;
                 }
@@ -1459,6 +1443,6 @@ public class GSMPhone extends PhoneBase {
     }
 
     public boolean isCspPlmnEnabled() {
-        return mSIMRecords.isCspPlmnEnabled();
+        return mIccRecords.isCspPlmnEnabled();
     }
 }
index 7dc2504..0870d5b 100644 (file)
@@ -374,11 +374,11 @@ public class GsmConnection extends Connection {
                 } else if (phone.getIccCard().getState() != SimCard.State.READY) {
                     return DisconnectCause.ICC_ERROR;
                 } else if (causeCode == CallFailCause.ERROR_UNSPECIFIED) {
-                    if (phone.mSST.rs.isCsRestricted()) {
+                    if (phone.mSST.mRestrictedState.isCsRestricted()) {
                         return DisconnectCause.CS_RESTRICTED;
-                    } else if (phone.mSST.rs.isCsEmergencyRestricted()) {
+                    } else if (phone.mSST.mRestrictedState.isCsEmergencyRestricted()) {
                         return DisconnectCause.CS_RESTRICTED_EMERGENCY;
-                    } else if (phone.mSST.rs.isCsNormalRestricted()) {
+                    } else if (phone.mSST.mRestrictedState.isCsNormalRestricted()) {
                         return DisconnectCause.CS_RESTRICTED_NORMAL;
                     } else {
                         return DisconnectCause.ERROR_UNSPECIFIED;
index 4689b2d..9695344 100644 (file)
@@ -19,10 +19,11 @@ package com.android.internal.telephony.gsm;
 import android.os.Message;
 import android.util.Log;
 import android.util.Patterns;
+import android.text.TextUtils;
 
-import com.android.internal.telephony.ApnSetting;
 import com.android.internal.telephony.DataConnection;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneBase;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.RetryManager;
 
@@ -34,11 +35,11 @@ public class GsmDataConnection extends DataConnection {
     private static final String LOG_TAG = "GSM";
 
     //***** Instance Variables
-    private ApnSetting apn;
-
+    protected int mProfileId = RILConstants.DATA_PROFILE_DEFAULT;
+    protected String mActiveApnType = Phone.APN_TYPE_DEFAULT;
     //***** Constructor
-    private GsmDataConnection(GSMPhone phone, String name, RetryManager rm) {
-        super(phone, name, rm);
+    private GsmDataConnection(PhoneBase phone, String name, int id, RetryManager rm) {
+        super(phone, name, id, rm);
     }
 
     /**
@@ -49,15 +50,13 @@ public class GsmDataConnection extends DataConnection {
      * @param rm the RetryManager
      * @return GsmDataConnection that was created.
      */
-    static GsmDataConnection makeDataConnection(GSMPhone phone, int id, RetryManager rm) {
+    static GsmDataConnection makeDataConnection(PhoneBase phone, int id, RetryManager rm) {
         synchronized (mCountLock) {
             mCount += 1;
         }
-        GsmDataConnection gsmDc = new GsmDataConnection(phone, "GsmDataConnection-" + mCount, rm);
+        GsmDataConnection gsmDc = new GsmDataConnection(phone, "GsmDC-" + mCount, id, rm);
         gsmDc.start();
         if (DBG) gsmDc.log("Made " + gsmDc.getName());
-        gsmDc.mId = id;
-        gsmDc.mRetryMgr = rm;
         return gsmDc;
     }
 
@@ -71,13 +70,13 @@ public class GsmDataConnection extends DataConnection {
     @Override
     protected
     void onConnect(ConnectionParams cp) {
-        apn = cp.apn;
+        mApn = cp.apn;
 
-        if (DBG) log("Connecting to carrier: '" + apn.carrier
-                + "' APN: '" + apn.apn
-                + "' proxy: '" + apn.proxy + "' port: '" + apn.port);
+        if (DBG) log("Connecting to carrier: '" + mApn.carrier
+                + "' APN: '" + mApn.apn
+                + "' proxy: '" + mApn.proxy + "' port: '" + mApn.port);
 
-        setHttpProxy (apn.proxy, apn.port);
+        setHttpProxy (mApn.proxy, mApn.port);
 
         createTime = -1;
         lastFailTime = -1;
@@ -87,35 +86,42 @@ public class GsmDataConnection extends DataConnection {
         Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
         msg.obj = cp;
 
-        int authType = apn.authType;
+        int authType = mApn.authType;
         if (authType == -1) {
-            authType = (apn.user != null) ? RILConstants.SETUP_DATA_AUTH_PAP_CHAP :
+            authType = (mApn.user != null) ? RILConstants.SETUP_DATA_AUTH_PAP_CHAP :
                 RILConstants.SETUP_DATA_AUTH_NONE;
         }
 
         String protocol;
         if (phone.getServiceState().getRoaming()) {
-            protocol = apn.roamingProtocol;
+            protocol = mApn.roamingProtocol;
         } else {
-            protocol = apn.protocol;
+            protocol = mApn.protocol;
         }
 
         phone.mCM.setupDataCall(
-                Integer.toString(RILConstants.SETUP_DATA_TECH_GSM),
-                Integer.toString(RILConstants.DATA_PROFILE_DEFAULT),
-                apn.apn, apn.user, apn.password, Integer.toString(authType),
+                Integer.toString(getRadioTechnology(RILConstants.SETUP_DATA_TECH_GSM)),
+                Integer.toString(mProfileId),
+                mApn.apn, mApn.user, mApn.password,
+                Integer.toString(authType),
                 protocol, msg);
     }
 
-    @Override
-    protected void clearSettings() {
-        super.clearSettings();
-        apn = null;
+    public void setProfileId(int profileId) {
+        mProfileId = profileId;
+    }
+
+    public int getProfileId() {
+        return mProfileId;
+    }
+
+    public void setActiveApnType(String apnType) {
+        mActiveApnType = apnType;
     }
 
     @Override
     public String toString() {
-        return "State=" + getCurrentState().getName() + " Apn=" + apn +
+        return "State=" + getCurrentState().getName() + " Apn=" + mApn +
                " create=" + createTime + " lastFail=" + lastFailTime +
                " lastFailCause=" + lastFailCause;
     }
@@ -123,17 +129,18 @@ public class GsmDataConnection extends DataConnection {
     @Override
     protected boolean isDnsOk(String[] domainNameServers) {
         if (NULL_IP.equals(domainNameServers[0]) && NULL_IP.equals(domainNameServers[1])
-                && !((GSMPhone) phone).isDnsCheckDisabled()) {
+                && !phone.isDnsCheckDisabled()) {
             // Work around a race condition where QMI does not fill in DNS:
             // Deactivate PDP and let DataConnectionTracker retry.
             // 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)) {
+            if (!mApn.types[0].equals(Phone.APN_TYPE_MMS)
+                || !isIpAddress(mApn.mmsProxy)) {
                 log(String.format(
                         "isDnsOk: return false apn.types[0]=%s APN_TYPE_MMS=%s isIpAddress(%s)=%s",
-                        apn.types[0], Phone.APN_TYPE_MMS, apn.mmsProxy, isIpAddress(apn.mmsProxy)));
+                        mApn.types[0], Phone.APN_TYPE_MMS, mApn.mmsProxy,
+                        isIpAddress(mApn.mmsProxy)));
                 return false;
             }
         }
@@ -145,27 +152,41 @@ public class GsmDataConnection extends DataConnection {
         Log.d(LOG_TAG, "[" + getName() + "] " + s);
     }
 
-    public ApnSetting getApn() {
-        return this.apn;
-    }
-
     private void setHttpProxy(String httpProxy, String httpPort) {
-        if (httpProxy == null || httpProxy.length() == 0) {
-            phone.setSystemProperty("net.gprs.http-proxy", null);
-            return;
-        }
 
-        if (httpPort == null || httpPort.length() == 0) {
-            httpPort = "8080";     // Default to port 8080
-        }
+        if (DBG) log("set http proxy for"
+                + "' APN: '" + mActiveApnType
+                + "' proxy: '" + mApn.proxy + "' port: '" + mApn.port);
+        if(TextUtils.equals(mActiveApnType, Phone.APN_TYPE_DEFAULT)) {
+            if (httpProxy == null || httpProxy.length() == 0) {
+                phone.setSystemProperty("net.gprs.http-proxy", null);
+                return;
+            }
+
+            if (httpPort == null || httpPort.length() == 0) {
+                httpPort = "8080";     // Default to port 8080
+            }
 
-        phone.setSystemProperty("net.gprs.http-proxy",
-                "http://" + httpProxy + ":" + httpPort + "/");
+            phone.setSystemProperty("net.gprs.http-proxy",
+                    "http://" + httpProxy + ":" + httpPort + "/");
+        } else {
+            if (httpProxy == null || httpProxy.length() == 0) {
+                phone.setSystemProperty("net.gprs.http-proxy." + mActiveApnType, null);
+                return;
+            }
+
+            if (httpPort == null || httpPort.length() == 0) {
+                httpPort = "8080";  // Default to port 8080
+            }
+
+            phone.setSystemProperty("net.gprs.http-proxy." + mActiveApnType,
+                    "http://" + httpProxy + ":" + httpPort + "/");
+        }
     }
 
     private boolean isIpAddress(String address) {
         if (address == null) return false;
 
-        return Patterns.IP_ADDRESS.matcher(apn.mmsProxy).matches();
+        return Patterns.IP_ADDRESS.matcher(address).matches();
     }
 }
index c57f2f1..df5898b 100644 (file)
@@ -22,38 +22,56 @@ import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.database.ContentObserver;
 import android.database.Cursor;
+import android.net.ConnectivityManager;
 import android.net.ProxyProperties;
 import android.net.TrafficStats;
 import android.net.Uri;
+import android.net.LinkCapabilities;
+import android.net.LinkProperties;
+import android.net.NetworkConfig;
 import android.os.AsyncResult;
 import android.os.Message;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.provider.Settings;
 import android.provider.Telephony;
+import android.telephony.CellLocation;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
+import android.telephony.cdma.CdmaCellLocation;
 import android.telephony.gsm.GsmCellLocation;
+import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Log;
+import android.preference.PreferenceManager;
 
 import com.android.internal.R;
+import com.android.internal.telephony.ApnContext;
 import com.android.internal.telephony.ApnSetting;
 import com.android.internal.telephony.DataCallState;
 import com.android.internal.telephony.DataConnection;
+import com.android.internal.telephony.DataConnectionAc;
 import com.android.internal.telephony.DataConnectionTracker;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneBase;
 import com.android.internal.telephony.RetryManager;
 import com.android.internal.telephony.EventLogTags;
 import com.android.internal.telephony.DataConnection.FailCause;
+import com.android.internal.telephony.RILConstants;
+import com.android.internal.util.AsyncChannel;
 
 import java.io.IOException;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.HashMap;
 
 /**
@@ -62,7 +80,6 @@ import java.util.HashMap;
 public final class GsmDataConnectionTracker extends DataConnectionTracker {
     protected final String LOG_TAG = "GSM";
 
-    private GSMPhone mGsmPhone;
     /**
      * Handles changes to the APN db.
      */
@@ -86,70 +103,69 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
     // call reRegisterNetwork, or pingTest succeeds.
     private int mPdpResetCount = 0;
 
-    /** Delay between APN attempts */
-    protected static final int APN_DELAY_MILLIS = 5000;
+    // Recovery action taken in case of data stall
+    enum RecoveryAction {REREGISTER, RADIO_RESTART, RADIO_RESET};
+    private RecoveryAction mRecoveryAction = RecoveryAction.REREGISTER;
 
-    //useful for debugging
-    boolean mFailNextConnect = false;
-
-    /**
-     * allApns holds all apns for this sim spn, retrieved from
-     * the Carrier DB.
-     *
-     * Create once after simcard info is loaded
-     */
-    private ArrayList<ApnSetting> mAllApns = null;
-
-    /**
-     * waitingApns holds all apns that are waiting to be connected
-     *
-     * It is a subset of allApns and has the same format
-     */
-    private ArrayList<ApnSetting> mWaitingApns = null;
-    private int mWaitingApnsPermanentFailureCountDown = 0;
-    private ApnSetting mPreferredApn = null;
-
-      /** The DataConnection being setup */
-    private GsmDataConnection mPendingDataConnection;
-
-    /** Convert an ApnType string to Id (TODO: Use "enumeration" instead of String for ApnType) */
-    private HashMap<String, Integer> mApnToDataConnectionId =
-                                    new HashMap<String, Integer>();
-
-    /** Is packet service restricted by network */
-    private boolean mIsPsRestricted = false;
 
     //***** Constants
 
     private static final int POLL_PDP_MILLIS = 5 * 1000;
 
     private static final String INTENT_RECONNECT_ALARM = "com.android.internal.telephony.gprs-reconnect";
+    private static final String INTENT_RECONNECT_ALARM_EXTRA_TYPE = "type";
 
     static final Uri PREFERAPN_URI = Uri.parse("content://telephony/carriers/preferapn");
     static final String APN_ID = "apn_id";
     private boolean canSetPreferApn = false;
+    private boolean mRadioAvailable = false;
+
+    @Override
+    protected void onActionIntentReconnectAlarm(Intent intent) {
+        if (DBG) log("GPRS reconnect alarm. Previous state was " + mState);
+
+        String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON);
+        int connectionId = intent.getIntExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, -1);
+
+        DataConnectionAc dcac= mDataConnectionAsyncChannels.get(connectionId);
+
+        if (dcac != null) {
+            for (ApnContext apnContext : dcac.getApnListSync()) {
+                apnContext.setReason(reason);
+                if (apnContext.getState() == State.FAILED) {
+                    apnContext.setState(State.IDLE);
+                }
+                sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext));
+            }
+            // Alram had expired. Clear pending intent recorded on the DataConnection.
+            dcac.setReconnectIntentSync(null);
+        }
+    }
 
     /** Watches for changes to the APN db. */
     private ApnChangeObserver mApnObserver;
 
     //***** Constructor
 
-    GsmDataConnectionTracker(GSMPhone p) {
+    public GsmDataConnectionTracker(PhoneBase p) {
         super(p);
-        mGsmPhone = p;
 
         p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null);
         p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
-        p.mSIMRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);
+        p.mIccRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);
         p.mCM.registerForDataNetworkStateChanged (this, EVENT_DATA_STATE_CHANGED, null);
-        p.mCT.registerForVoiceCallEnded (this, EVENT_VOICE_CALL_ENDED, null);
-        p.mCT.registerForVoiceCallStarted (this, EVENT_VOICE_CALL_STARTED, null);
-        p.mSST.registerForGprsAttached(this, EVENT_GPRS_ATTACHED, null);
-        p.mSST.registerForGprsDetached(this, EVENT_GPRS_DETACHED, null);
-        p.mSST.registerForRoamingOn(this, EVENT_ROAMING_ON, null);
-        p.mSST.registerForRoamingOff(this, EVENT_ROAMING_OFF, null);
-        p.mSST.registerForPsRestrictedEnabled(this, EVENT_PS_RESTRICT_ENABLED, null);
-        p.mSST.registerForPsRestrictedDisabled(this, EVENT_PS_RESTRICT_DISABLED, null);
+        p.getCallTracker().registerForVoiceCallEnded (this, EVENT_VOICE_CALL_ENDED, null);
+        p.getCallTracker().registerForVoiceCallStarted (this, EVENT_VOICE_CALL_STARTED, null);
+        p.getServiceStateTracker().registerForDataConnectionAttached(this,
+                EVENT_DATA_CONNECTION_ATTACHED, null);
+        p.getServiceStateTracker().registerForDataConnectionDetached(this,
+                EVENT_DATA_CONNECTION_DETACHED, null);
+        p.getServiceStateTracker().registerForRoamingOn(this, EVENT_ROAMING_ON, null);
+        p.getServiceStateTracker().registerForRoamingOff(this, EVENT_ROAMING_OFF, null);
+        p.getServiceStateTracker().registerForPsRestrictedEnabled(this,
+                EVENT_PS_RESTRICT_ENABLED, null);
+        p.getServiceStateTracker().registerForPsRestrictedDisabled(this,
+                EVENT_PS_RESTRICT_DISABLED, null);
 
         mDataConnectionTracker = this;
         mResolver = mPhone.getContext().getContentResolver();
@@ -158,35 +174,68 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
         p.getContext().getContentResolver().registerContentObserver(
                 Telephony.Carriers.CONTENT_URI, true, mApnObserver);
 
-        /** Create the default connection */
-        createDataConnection(Phone.APN_TYPE_DEFAULT);
+        mApnContexts = new ConcurrentHashMap<String, ApnContext>();
+        initApnContextsAndDataConnection();
         broadcastMessenger();
     }
 
     @Override
     public void dispose() {
+        cleanUpAllConnections(false, null);
+
         super.dispose();
 
         //Unregister for all events
         mPhone.mCM.unregisterForAvailable(this);
         mPhone.mCM.unregisterForOffOrNotAvailable(this);
-        mGsmPhone.mSIMRecords.unregisterForRecordsLoaded(this);
+        mPhone.mIccRecords.unregisterForRecordsLoaded(this);
         mPhone.mCM.unregisterForDataNetworkStateChanged(this);
-        mGsmPhone.mCT.unregisterForVoiceCallEnded(this);
-        mGsmPhone.mCT.unregisterForVoiceCallStarted(this);
-        mGsmPhone.mSST.unregisterForGprsAttached(this);
-        mGsmPhone.mSST.unregisterForGprsDetached(this);
-        mGsmPhone.mSST.unregisterForRoamingOn(this);
-        mGsmPhone.mSST.unregisterForRoamingOff(this);
-        mGsmPhone.mSST.unregisterForPsRestrictedEnabled(this);
-        mGsmPhone.mSST.unregisterForPsRestrictedDisabled(this);
+        mPhone.getCallTracker().unregisterForVoiceCallEnded(this);
+        mPhone.getCallTracker().unregisterForVoiceCallStarted(this);
+        mPhone.getServiceStateTracker().unregisterForDataConnectionAttached(this);
+        mPhone.getServiceStateTracker().unregisterForDataConnectionDetached(this);
+        mPhone.getServiceStateTracker().unregisterForRoamingOn(this);
+        mPhone.getServiceStateTracker().unregisterForRoamingOff(this);
+        mPhone.getServiceStateTracker().unregisterForPsRestrictedEnabled(this);
+        mPhone.getServiceStateTracker().unregisterForPsRestrictedDisabled(this);
 
         mPhone.getContext().getContentResolver().unregisterContentObserver(this.mApnObserver);
+        mApnContexts.clear();
 
         destroyDataConnections();
     }
 
     @Override
+    public boolean isApnTypeActive(String type) {
+        ApnContext apnContext = mApnContexts.get(type);
+        if (apnContext == null) return false;
+
+        return (apnContext.getDataConnection() != null);
+    }
+
+    @Override
+    protected boolean isDataPossible(String apnType) {
+        ApnContext apnContext = mApnContexts.get(apnType);
+        if (apnContext == null) {
+            return false;
+        }
+        boolean apnContextIsEnabled = apnContext.isEnabled();
+        State apnContextState = apnContext.getState();
+        boolean apnTypePossible = !(apnContextIsEnabled &&
+                (apnContextState == State.FAILED));
+        boolean dataAllowed = isDataAllowed();
+        boolean possible = dataAllowed && apnTypePossible;
+
+        if (DBG) {
+            log(String.format("isDataPossible(%s): possible=%b isDataAllowed=%b " +
+                    "apnTypePossible=%b apnContextisEnabled=%b apnContextState()=%s",
+                    apnType, possible, dataAllowed, apnTypePossible,
+                    apnContextIsEnabled, apnContextState));
+        }
+        return possible;
+    }
+
+    @Override
     protected void finalize() {
         if(DBG) log("finalize");
     }
@@ -196,47 +245,262 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
         return INTENT_RECONNECT_ALARM;
     }
 
+    private ApnContext addApnContext(String type) {
+        ApnContext apnContext = new ApnContext(type, LOG_TAG);
+        apnContext.setDependencyMet(false);
+        mApnContexts.put(type, apnContext);
+        return apnContext;
+    }
+
+    protected void initApnContextsAndDataConnection() {
+        boolean defaultEnabled = SystemProperties.getBoolean(DEFALUT_DATA_ON_BOOT_PROP, true);
+        // Load device network attributes from resources
+        String[] networkConfigStrings = mPhone.getContext().getResources().getStringArray(
+                com.android.internal.R.array.networkAttributes);
+        for (String networkConfigString : networkConfigStrings) {
+            NetworkConfig networkConfig = new NetworkConfig(networkConfigString);
+            ApnContext apnContext = null;
+
+            switch (networkConfig.type) {
+            case ConnectivityManager.TYPE_MOBILE:
+                apnContext = addApnContext(Phone.APN_TYPE_DEFAULT);
+                apnContext.setEnabled(defaultEnabled);
+                break;
+            case ConnectivityManager.TYPE_MOBILE_MMS:
+                apnContext = addApnContext(Phone.APN_TYPE_MMS);
+                break;
+            case ConnectivityManager.TYPE_MOBILE_SUPL:
+                apnContext = addApnContext(Phone.APN_TYPE_SUPL);
+                break;
+            case ConnectivityManager.TYPE_MOBILE_DUN:
+                apnContext = addApnContext(Phone.APN_TYPE_DUN);
+                break;
+            case ConnectivityManager.TYPE_MOBILE_HIPRI:
+                apnContext = addApnContext(Phone.APN_TYPE_HIPRI);
+                break;
+            case ConnectivityManager.TYPE_MOBILE_FOTA:
+                apnContext = addApnContext(Phone.APN_TYPE_FOTA);
+                break;
+            case ConnectivityManager.TYPE_MOBILE_IMS:
+                apnContext = addApnContext(Phone.APN_TYPE_IMS);
+                break;
+            case ConnectivityManager.TYPE_MOBILE_CBS:
+                apnContext = addApnContext(Phone.APN_TYPE_CBS);
+                break;
+            default:
+                // skip unknown types
+                continue;
+            }
+            if (apnContext != null) {
+                // set the prop, but also apply the newly set enabled and dependency values
+                onSetDependencyMet(apnContext.getApnType(), networkConfig.dependencyMet);
+            }
+        }
+    }
+
+    @Override
+    protected LinkProperties getLinkProperties(String apnType) {
+        ApnContext apnContext = mApnContexts.get(apnType);
+        if (apnContext != null) {
+            DataConnectionAc dcac = apnContext.getDataConnectionAc();
+            if (dcac != null) {
+                if (DBG) log("return link properites for " + apnType);
+                return dcac.getLinkPropertiesSync();
+            }
+        }
+        if (DBG) log("return new LinkProperties");
+        return new LinkProperties();
+    }
+
+    @Override
+    protected LinkCapabilities getLinkCapabilities(String apnType) {
+        ApnContext apnContext = mApnContexts.get(apnType);
+        if (apnContext!=null) {
+            DataConnectionAc dataConnectionAc = apnContext.getDataConnectionAc();
+            if (dataConnectionAc != null) {
+                if (DBG) log("get active pdp is not null, return link Capabilities for " + apnType);
+                return dataConnectionAc.getLinkCapabilitiesSync();
+            }
+        }
+        if (DBG) log("return new LinkCapabilities");
+        return new LinkCapabilities();
+    }
+
+    @Override
+    // Return all active apn types
+    public String[] getActiveApnTypes() {
+        if (DBG) log("get all active apn types");
+        ArrayList<String> result = new ArrayList<String>();
+
+        for (ApnContext apnContext : mApnContexts.values()) {
+            if (apnContext.isReady()) {
+                result.add(apnContext.getApnType());
+            }
+        }
+
+        return (String[])result.toArray(new String[0]);
+    }
+
+    @Override
+    // Return active apn of specific apn type
+    public String getActiveApnString(String apnType) {
+        if (DBG) log( "get active apn string for type:" + apnType);
+        ApnContext apnContext = mApnContexts.get(apnType);
+        if (apnContext != null) {
+            ApnSetting apnSetting = apnContext.getApnSetting();
+            if (apnSetting != null) {
+                return apnSetting.apn;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public boolean isApnTypeEnabled(String apnType) {
+        ApnContext apnContext = mApnContexts.get(apnType);
+        if (apnContext == null) {
+            return false;
+        }
+        return apnContext.isEnabled();
+    }
+
     @Override
     protected void setState(State s) {
-        if (DBG) log ("setState: " + s);
-        if (mState != s) {
-            EventLog.writeEvent(EventLogTags.GSM_DATA_STATE_CHANGE, mState.toString(), s.toString());
-            mState = s;
+        if (DBG) log("setState should not be used in GSM" + s);
+    }
+
+    // Return state of specific apn type
+    @Override
+    public State getState(String apnType) {
+        ApnContext apnContext = mApnContexts.get(apnType);
+        if (apnContext != null) {
+            return apnContext.getState();
         }
+        return State.FAILED;
+    }
 
-        if (mState == State.FAILED) {
-            if (mWaitingApns != null)
-                mWaitingApns.clear(); // when tear down the connection and set to IDLE
+    // Return state of overall
+    public State getOverallState() {
+        boolean isConnecting = false;
+        boolean isFailed = true; // All enabled Apns should be FAILED.
+        boolean isAnyEnabled = false;
+
+        for (ApnContext apnContext : mApnContexts.values()) {
+            if (apnContext.isEnabled()) {
+                isAnyEnabled = true;
+                switch (apnContext.getState()) {
+                case CONNECTED:
+                case DISCONNECTING:
+                    if (DBG) log("overall state is CONNECTED");
+                    return State.CONNECTED;
+                case CONNECTING:
+                case INITING:
+                    isConnecting = true;
+                    isFailed = false;
+                    break;
+                case IDLE:
+                case SCANNING:
+                    isFailed = false;
+                    break;
+                }
+            }
+        }
+
+        if (!isAnyEnabled) { // Nothing enabled. return IDLE.
+            if (DBG) log( "overall state is IDLE");
+            return State.IDLE;
+        }
+
+        if (isConnecting) {
+            if (DBG) log( "overall state is CONNECTING");
+            return State.CONNECTING;
+        } else if (!isFailed) {
+            if (DBG) log( "overall state is IDLE");
+            return State.IDLE;
+        } else {
+            if (DBG) log( "overall state is FAILED");
+            return State.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
+     * Ensure that we are connected to an APN of the specified type.
      *
-     * @return false while no data connection if all above requirements are met.
+     * @param type the APN type
+     * @return Success is indicated by {@code Phone.APN_ALREADY_ACTIVE} or
+     *         {@code Phone.APN_REQUEST_STARTED}. In the latter case, a
+     *         broadcast will be sent by the ConnectivityManager when a
+     *         connection to the APN has been established.
      */
     @Override
-    public boolean isDataConnectionAsDesired() {
-        boolean roaming = mPhone.getServiceState().getRoaming();
+    public synchronized int enableApnType(String apnType) {
+        ApnContext apnContext = mApnContexts.get(apnType);
+        if (apnContext == null || !isApnTypeAvailable(apnType)) {
+            if (DBG) log("enableApnType: " + apnType + " is type not available");
+            return Phone.APN_TYPE_NOT_AVAILABLE;
+        }
+
+        // If already active, return
+        if (DBG) log("enableApnType: " + apnType + " mState(" + apnContext.getState() + ")");
 
-        if (mGsmPhone.mSIMRecords.getRecordsLoaded() &&
-                mGsmPhone.mSST.getCurrentGprsState() == ServiceState.STATE_IN_SERVICE &&
-                (!roaming || getDataOnRoamingEnabled()) &&
-            !mIsWifiConnected &&
-            !mIsPsRestricted ) {
-            return (mState == State.CONNECTED);
+        if (apnContext.getState() == State.CONNECTED) {
+            if (DBG) log("enableApnType: return APN_ALREADY_ACTIVE");
+            return Phone.APN_ALREADY_ACTIVE;
+        }
+        setEnabled(apnTypeToId(apnType), true);
+        if (DBG) {
+            log("enableApnType: new apn request for type " + apnType +
+                    " return APN_REQUEST_STARTED");
+        }
+        return Phone.APN_REQUEST_STARTED;
+    }
+
+    // A new APN has gone active and needs to send events to catch up with the
+    // current condition
+    private void notifyApnIdUpToCurrent(String reason, ApnContext apnContext, String type) {
+        switch (apnContext.getState()) {
+            case IDLE:
+            case INITING:
+                break;
+            case CONNECTING:
+            case SCANNING:
+                mPhone.notifyDataConnection(reason, type, Phone.DataState.CONNECTING);
+                break;
+            case CONNECTED:
+            case DISCONNECTING:
+                mPhone.notifyDataConnection(reason, type, Phone.DataState.CONNECTING);
+                mPhone.notifyDataConnection(reason, type, Phone.DataState.CONNECTED);
+                break;
+        }
+    }
+
+    @Override
+    public synchronized int disableApnType(String type) {
+        if (DBG) log("disableApnType:" + type);
+        ApnContext apnContext = mApnContexts.get(type);
+
+        if (apnContext != null) {
+            setEnabled(apnTypeToId(type), false);
+            if (apnContext.getState() != State.IDLE && apnContext.getState() != State.FAILED) {
+                if (DBG) log("diableApnType: return APN_REQUEST_STARTED");
+                return Phone.APN_REQUEST_STARTED;
+            } else {
+                if (DBG) log("disableApnType: return APN_ALREADY_INACTIVE");
+                return Phone.APN_ALREADY_INACTIVE;
+            }
+
+        } else {
+            if (DBG) {
+                log("disableApnType: no apn context was found, return APN_REQUEST_FAILED");
+            }
+            return Phone.APN_REQUEST_FAILED;
         }
-        return true;
     }
 
     @Override
     protected boolean isApnTypeAvailable(String type) {
-        if (type.equals(Phone.APN_TYPE_DUN)) {
-            return (fetchDunApn() != null);
+        if (type.equals(Phone.APN_TYPE_DUN) && fetchDunApn() != null) {
+            return true;
         }
 
         if (mAllApns != null) {
@@ -249,41 +513,66 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
         return false;
     }
 
+    /**
+     * Report on whether data connectivity is enabled for any APN.
+     * @return {@code false} if data connectivity has been explicitly disabled,
+     * {@code true} otherwise.
+     */
+    @Override
+    public synchronized boolean getAnyDataEnabled() {
+        if (!(mInternalDataEnabled && mDataEnabled)) return false;
+        for (ApnContext apnContext : mApnContexts.values()) {
+            // Make sure we dont have a context that going down
+            // and is explicitly disabled.
+            if (isDataAllowed(apnContext)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isDataAllowed(ApnContext apnContext) {
+        return apnContext.isReady() && isDataAllowed();
+    }
+
     //****** Called from ServiceStateTracker
     /**
      * Invoked when ServiceStateTracker observes a transition from GPRS
      * attach to detach.
      */
-    protected void onGprsDetached() {
+    protected void onDataConnectionDetached() {
         /*
          * We presently believe it is unnecessary to tear down the PDP context
          * when GPRS detaches, but we should stop the network polling.
          */
+        if (DBG) log ("onDataConnectionDetached: stop polling and notify detached");
         stopNetStatPoll();
-        notifyDataConnection(Phone.REASON_GPRS_DETACHED);
+        notifyDataConnection(Phone.REASON_DATA_DETACHED);
     }
 
-    private void onGprsAttached() {
-        if (mState == State.CONNECTED) {
+    private void onDataConnectionAttached() {
+        if (DBG) log("onDataConnectionAttached");
+        if (getOverallState() == State.CONNECTED) {
+            if (DBG) log("onDataConnectionAttached: start polling notify attached");
             startNetStatPoll();
-            notifyDataConnection(Phone.REASON_GPRS_ATTACHED);
+            notifyDataConnection(Phone.REASON_DATA_ATTACHED);
         } else {
-            if (mState == State.FAILED) {
-                cleanUpConnection(false, Phone.REASON_GPRS_ATTACHED);
-                mRetryMgr.resetRetryCount();
-            }
-            trySetupData(Phone.REASON_GPRS_ATTACHED);
+            // update APN availability so that APN can be enabled.
+            notifyDataAvailability(Phone.REASON_DATA_ATTACHED);
         }
+
+        setupDataOnReadyApns(Phone.REASON_DATA_ATTACHED);
     }
 
     @Override
     protected boolean isDataAllowed() {
-        int gprsState = mGsmPhone.mSST.getCurrentGprsState();
-        boolean desiredPowerState = mGsmPhone.mSST.getDesiredPowerState();
+        int gprsState = mPhone.getServiceStateTracker().getCurrentDataConnectionState();
+        boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
 
         boolean allowed =
                     (gprsState == ServiceState.STATE_IN_SERVICE || mAutoAttachOnCreation) &&
-                    mGsmPhone.mSIMRecords.getRecordsLoaded() &&
+                    mPhone.mIccRecords.getRecordsLoaded() &&
+                    mPhone.mIccRecords.isProvisioned() &&
                     mPhone.getState() == Phone.State.IDLE &&
                     mInternalDataEnabled &&
                     (!mPhone.getServiceState().getRoaming() || getDataOnRoamingEnabled()) &&
@@ -294,7 +583,8 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
             if (!((gprsState == ServiceState.STATE_IN_SERVICE) || mAutoAttachOnCreation)) {
                 reason += " - gprs= " + gprsState;
             }
-            if (!mGsmPhone.mSIMRecords.getRecordsLoaded()) reason += " - SIM not loaded";
+            if (!mPhone.mIccRecords.getRecordsLoaded()) reason += " - SIM not loaded";
+            if (!mPhone.mIccRecords.isProvisioned()) reason += " - SIM not provisioned";
             if (mPhone.getState() != Phone.State.IDLE) {
                 reason += " - PhoneState= " + mPhone.getState();
             }
@@ -304,96 +594,245 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
             }
             if (mIsPsRestricted) reason += " - mIsPsRestricted= true";
             if (!desiredPowerState) reason += " - desiredPowerState= false";
-            log("Data not allowed due to" + reason);
+            if (DBG) log("isDataAllowed: not allowed due to" + reason);
         }
         return allowed;
     }
 
-    private boolean trySetupData(String reason) {
-        if (DBG) log("***trySetupData due to " + (reason == null ? "(unspecified)" : reason));
+    private void setupDataOnReadyApns(String reason) {
+        // Stop reconnect alarms on all data connections pending
+        // retry. Reset ApnContext state to IDLE.
+        for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
+            if (dcac.getReconnectIntentSync() != null) {
+                cancelReconnectAlarm(dcac);
+                if (dcac.dataConnection != null) {
+                    dcac.dataConnection.resetRetryCount();
+                }
+            }
+        }
+
+        // Only check for default APN state
+        for (ApnContext apnContext : mApnContexts.values()) {
+            if (apnContext.getState() == State.FAILED) {
+                // By this time, alarms for all failed Apns
+                // should be stopped if any.
+                // Make sure to set the state back to IDLE
+                // so that setup data can happen.
+                apnContext.setState(State.IDLE);
+            }
+            if (apnContext.isReady()) {
+                if (apnContext.getState() == State.IDLE) {
+                    apnContext.setReason(reason);
+                    trySetupData(apnContext);
+                }
+            }
+        }
+    }
+
+    private boolean trySetupData(String reason, String type) {
+        if (DBG) {
+            log("trySetupData: " + type + " due to " + (reason == null ? "(unspecified)" : reason)
+                    + " isPsRestricted=" + mIsPsRestricted);
+        }
+
+        if (type == null) {
+            type = Phone.APN_TYPE_DEFAULT;
+        }
+
+        ApnContext apnContext = mApnContexts.get(type);
 
-        log("[DSAC DEB] " + "trySetupData with mIsPsRestricted=" + mIsPsRestricted);
+        if (apnContext == null ){
+            if (DBG) log("trySetupData new apn context for type:" + type);
+            apnContext = new ApnContext(type, LOG_TAG);
+            mApnContexts.put(type, apnContext);
+        }
+        apnContext.setReason(reason);
+
+        return trySetupData(apnContext);
+    }
+
+    private boolean trySetupData(ApnContext apnContext) {
+        if (DBG) {
+            log("trySetupData for type:" + apnContext.getApnType() +
+                    " due to " + apnContext.getReason());
+            log("trySetupData with mIsPsRestricted=" + mIsPsRestricted);
+        }
 
         if (mPhone.getSimulatedRadioControl() != null) {
             // Assume data is connected on the simulator
             // FIXME  this can be improved
-            setState(State.CONNECTED);
-            notifyDataConnection(reason);
+            apnContext.setState(State.CONNECTED);
+            mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
 
-            log("(fix?) We're on the simulator; assuming data is connected");
+            log("trySetupData: (fix?) We're on the simulator; assuming data is connected");
             return true;
         }
 
-        int gprsState = mGsmPhone.mSST.getCurrentGprsState();
-        boolean desiredPowerState = mGsmPhone.mSST.getDesiredPowerState();
+        boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
 
-        if (((mState == State.IDLE) || (mState == State.SCANNING)) &&
-                isDataAllowed() && getAnyDataEnabled()) {
+        if ((apnContext.getState() == State.IDLE || apnContext.getState() == State.SCANNING) &&
+                isDataAllowed(apnContext) && getAnyDataEnabled()) {
 
-            if (mState == State.IDLE) {
-                mWaitingApns = buildWaitingApns(mRequestedApnType);
-                mWaitingApnsPermanentFailureCountDown = mWaitingApns.size();
-                if (mWaitingApns.isEmpty()) {
-                    if (DBG) log("No APN found");
-                    notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN);
-                    notifyOffApnsOfAvailability(reason, false);
+            if (apnContext.getState() == State.IDLE) {
+                ArrayList<ApnSetting> waitingApns = buildWaitingApns(apnContext.getApnType());
+                if (waitingApns.isEmpty()) {
+                    if (DBG) log("trySetupData: No APN found");
+                    notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN, apnContext);
+                    notifyOffApnsOfAvailability(apnContext.getReason(), false);
                     return false;
                 } else {
-                    log ("Create from allApns : " + apnListToString(mAllApns));
+                    apnContext.setWaitingApns(waitingApns);
+                    if (DBG) {
+                        log ("trySetupData: Create from mAllApns : " + apnListToString(mAllApns));
+                    }
                 }
             }
 
             if (DBG) {
-                log ("Setup waitngApns : " + apnListToString(mWaitingApns));
+                log ("Setup watingApns : " + apnListToString(apnContext.getWaitingApns()));
             }
-            boolean retValue = setupData(reason);
-            notifyOffApnsOfAvailability(reason, retValue);
+            // apnContext.setReason(apnContext.getReason());
+            boolean retValue = setupData(apnContext);
+            notifyOffApnsOfAvailability(apnContext.getReason(), retValue);
             return retValue;
         } else {
-            notifyOffApnsOfAvailability(reason, false);
+            // TODO: check the condition.
+            if (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT)
+                && (apnContext.getState() == State.IDLE
+                    || apnContext.getState() == State.SCANNING))
+                mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType());
+            notifyOffApnsOfAvailability(apnContext.getReason(), false);
             return false;
         }
     }
 
+    @Override
+    // Disabled apn's still need avail/unavail notificiations - send them out
+    protected void notifyOffApnsOfAvailability(String reason, boolean availability) {
+        if (mAvailability == availability) return;
+        mAvailability = availability;
+
+        for (ApnContext apnContext : mApnContexts.values()) {
+            if (!apnContext.isReady()) {
+                if (DBG) log("notifyOffApnOfAvailability type:" + apnContext.getApnType());
+                mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(),
+                                            apnContext.getApnType(),
+                                            Phone.DataState.DISCONNECTED);
+            }
+        }
+    }
+
+    /**
+     * If tearDown is true, this only tears down a CONNECTED session. Presently,
+     * there is no mechanism for abandoning an INITING/CONNECTING session,
+     * but would likely involve cancelling pending async requests or
+     * setting a flag or new state to ignore them when they came in
+     * @param tearDown true if the underlying GsmDataConnection should be
+     * disconnected.
+     * @param reason reason for the clean up.
+     */
+    protected void cleanUpAllConnections(boolean tearDown, String reason) {
+        if (DBG) log("cleanUpAllConnections: tearDown=" + tearDown + " reason=" + reason);
+
+        for (ApnContext apnContext : mApnContexts.values()) {
+            apnContext.setReason(reason);
+            cleanUpConnection(tearDown, apnContext);
+        }
+
+        stopNetStatPoll();
+        // TODO: Do we need mRequestedApnType?
+        mRequestedApnType = Phone.APN_TYPE_DEFAULT;
+    }
+
     /**
      * Cleanup all connections.
      *
      * TODO: Cleanup only a specified connection passed as a parameter.
+     *       Also, make sure when you clean up a conn, if it is last apply
+     *       logic as though it is cleanupAllConnections
      *
      * @param tearDown true if the underlying DataConnection should be disconnected.
      * @param reason for the clean up.
      */
-    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) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
-            am.cancel(mReconnectIntent);
-            mReconnectIntent = null;
-        }
-
-        setState(State.DISCONNECTING);
-
-        boolean notificationDeferred = false;
-        for (DataConnection conn : mDataConnections.values()) {
-            if (tearDown) {
-                if (DBG) log("cleanUpConnection: teardown, call conn.disconnect");
-                conn.disconnect(obtainMessage(EVENT_DISCONNECT_DONE,
-                        conn.getDataConnectionId(), 0, reason));
-                notificationDeferred = true;
+
+    @Override
+    protected void onCleanUpAllConnections(String cause) {
+        cleanUpAllConnections(true, cause);
+    }
+
+    private void cleanUpConnection(boolean tearDown, ApnContext apnContext) {
+
+        if (apnContext == null) {
+            if (DBG) log("cleanUpConnection: apn context is null");
+            return;
+        }
+
+        if (DBG) {
+            log("cleanUpConnection: tearDown=" + tearDown + " reason=" + apnContext.getReason());
+        }
+        DataConnectionAc dcac = apnContext.getDataConnectionAc();
+        if (tearDown) {
+            boolean isConnected = (apnContext.getState() != State.IDLE
+                                   && apnContext.getState() != State.FAILED);
+            if (!isConnected) {
+                // The request is tearDown and but ApnContext is not connected.
+                // If apnContext is not enabled anymore, break the linkage to the DCAC/DC.
+                apnContext.setState(State.IDLE);
+                if (!apnContext.isReady()) {
+                    apnContext.setDataConnection(null);
+                    apnContext.setDataConnectionAc(null);
+                }
             } else {
-                if (DBG) log("cleanUpConnection: !tearDown, call conn.resetSynchronously");
-                conn.resetSynchronously();
-                notificationDeferred = false;
+                // Connection is still there. Try to clean up.
+                if (dcac != null) {
+                    if (apnContext.getState() != State.DISCONNECTING) {
+                        if (DBG) log("cleanUpConnection: tearing down");
+                        Message msg = obtainMessage(EVENT_DISCONNECT_DONE, apnContext);
+                        apnContext.getDataConnection().tearDown(apnContext.getReason(), msg);
+                        apnContext.setState(State.DISCONNECTING);
+                    }
+                } else {
+                    // apn is connected but no reference to dcac.
+                    // Should not be happen, but reset the state in case.
+                    apnContext.setState(State.IDLE);
+                    mPhone.notifyDataConnection(apnContext.getReason(),
+                                                apnContext.getApnType());
+                }
             }
+        } else {
+            // force clean up the data connection.
+            if (dcac != null) dcac.resetSync();
+            apnContext.setState(State.IDLE);
+            mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
+            apnContext.setDataConnection(null);
+            apnContext.setDataConnectionAc(null);
         }
-        stopNetStatPoll();
 
-        if (!notificationDeferred) {
-            if (DBG) log("cleanupConnection: !notificationDeferred");
-            gotoIdleAndNotifyDataConnection(reason);
+        // make sure reconnection alarm is cleaned up if there is no ApnContext
+        // associated to the connection.
+        if (dcac != null) {
+            Collection<ApnContext> apnList = dcac.getApnListSync();
+            if (apnList.isEmpty()) {
+                cancelReconnectAlarm(dcac);
+            }
+        }
+    }
+
+    /**
+     * Cancels the alarm associated with DCAC.
+     *
+     * @param DataConnectionAc on which the alarm should be stopped.
+     */
+    private void cancelReconnectAlarm(DataConnectionAc dcac) {
+        if (dcac == null) return;
+
+        PendingIntent intent = dcac.getReconnectIntentSync();
+
+        if (intent != null) {
+                AlarmManager am =
+                    (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
+                am.cancel(intent);
+                dcac.setReconnectIntentSync(null);
         }
     }
 
@@ -439,162 +878,333 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
                 result.add(apn);
             } while (cursor.moveToNext());
         }
+        if (DBG) log("createApnList: X result=" + result);
         return result;
     }
 
+    private boolean dataConnectionNotInUse(DataConnectionAc dcac) {
+        for (ApnContext apnContext : mApnContexts.values()) {
+            if (apnContext.getDataConnectionAc() == dcac) return false;
+        }
+        return true;
+    }
+
     private GsmDataConnection findFreeDataConnection() {
-        for (DataConnection dc : mDataConnections.values()) {
-            if (dc.isInactive()) {
-                log("found free GsmDataConnection");
-                return (GsmDataConnection) dc;
+        for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
+            if (dcac.isInactiveSync() && dataConnectionNotInUse(dcac)) {
+                log("findFreeDataConnection: found free GsmDataConnection");
+                return (GsmDataConnection) dcac.dataConnection;
             }
         }
-        log("NO free GsmDataConnection");
+        log("findFreeDataConnection: NO free GsmDataConnection");
         return null;
     }
 
-    private boolean setupData(String reason) {
+    protected GsmDataConnection findReadyDataConnection(ApnSetting apn) {
+        if (DBG)
+            log("findReadyDataConnection: apn string <" +
+                (apn!=null?(apn.toString()):"null") +">");
+        if (apn == null) {
+            return null;
+        }
+        for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
+            ApnSetting apnSetting = dcac.getApnSettingSync();
+            if (DBG) {
+                log("findReadyDataConnection: dc apn string <" +
+                         (apnSetting != null ? (apnSetting.toString()) : "null") + ">");
+            }
+            if ((apnSetting != null) && TextUtils.equals(apnSetting.toString(), apn.toString())) {
+                return (GsmDataConnection) dcac.dataConnection;
+            }
+        }
+        return null;
+    }
+
+
+    private boolean setupData(ApnContext apnContext) {
+        if (DBG) log("setupData: apnContext=" + apnContext);
         ApnSetting apn;
-        GsmDataConnection gdc;
+        GsmDataConnection dc;
 
-        apn = getNextApn();
-        if (apn == null) return false;
-        gdc = findFreeDataConnection();
-        if (gdc == null) {
-            if (DBG) log("setupData: No free GsmDataConnection found!");
+        int profileId = getApnProfileID(apnContext.getApnType());
+        apn = apnContext.getNextWaitingApn();
+        if (apn == null) {
+            if (DBG) log("setupData: return for no apn found!");
             return false;
         }
-        mActiveApn = apn;
-        mPendingDataConnection = gdc;
 
-        Message msg = obtainMessage();
-        msg.what = EVENT_DATA_SETUP_COMPLETE;
-        msg.obj = reason;
-        gdc.connect(msg, apn);
+        // First, check to see if ApnContext already has DC.
+        // This could happen if the retries are currently  engaged.
+        dc = (GsmDataConnection)apnContext.getDataConnection();
 
-        setState(State.INITING);
-        notifyDataConnection(reason);
-        return true;
-    }
+        if (dc == null) {
 
-    private boolean dataCallStatesHasCID (ArrayList<DataCallState> states, int cid) {
-        for (int i = 0, s = states.size() ; i < s ; i++) {
-            if (states.get(i).cid == cid) return true;
-        }
-        return false;
-    }
+            dc = (GsmDataConnection) checkForConnectionForApnContext(apnContext);
 
-    private boolean dataCallStatesHasActiveCID (ArrayList<DataCallState> states, int cid) {
-        for (int i = 0, s = states.size() ; i < s ; i++) {
-            if ((states.get(i).cid == cid) && (states.get(i).active != 0)) {
-                return true;
+            if (dc == null) {
+                dc = findReadyDataConnection(apn);
+            }
+
+            if (dc == null) {
+                if (DBG) log("setupData: No ready GsmDataConnection found!");
+                // TODO: When allocating you are mapping type to id. If more than 1 free,
+                // then could findFreeDataConnection get the wrong one??
+                dc = findFreeDataConnection();
             }
+
+            if (dc == null) {
+                dc = createDataConnection();
+            }
+
+            if (dc == null) {
+                if (DBG) log("setupData: No free GsmDataConnection found!");
+                return false;
+            }
+
+            DataConnectionAc dcac = mDataConnectionAsyncChannels.get(dc.getDataConnectionId());
+            dc.setProfileId( profileId );
+            dc.setActiveApnType(apnContext.getApnType());
+            int refCount = dcac.getRefCountSync();
+            if (DBG) log("setupData: init dc and apnContext refCount=" + refCount);
+
+            // configure retry count if no other Apn is using the same connection.
+            if (refCount == 0) {
+                configureRetry(dc, apnContext.getApnType());
+            }
+            apnContext.setDataConnectionAc(dcac);
+            apnContext.setDataConnection(dc);
         }
 
-        return false;
+        apnContext.setApnSetting(apn);
+        apnContext.setState(State.INITING);
+        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
+        // If reconnect alarm is active on this DataConnection, wait for the alarm being
+        // fired so that we don't disruppt data retry pattern engaged.
+        if (apnContext.getDataConnectionAc().getReconnectIntentSync() != null) {
+            if (DBG) log("setupData: data reconnection pending");
+            apnContext.setState(State.FAILED);
+            mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
+            return true;
+        }
+
+        Message msg = obtainMessage();
+        msg.what = EVENT_DATA_SETUP_COMPLETE;
+        msg.obj = apnContext;
+        dc.bringUp(msg, apn);
+
+        if (DBG) log("setupData: initing!");
+        return true;
     }
 
     /**
      * Handles changes to the APN database.
      */
     private void onApnChanged() {
+        // TODO: How to handle when multiple APNs are active?
         boolean isConnected;
 
-        isConnected = (mState != State.IDLE && mState != State.FAILED);
+        ApnContext defaultApnContext = mApnContexts.get(Phone.APN_TYPE_DEFAULT);
+        isConnected = (defaultApnContext.getState() != State.IDLE
+                       && defaultApnContext.getState() != State.FAILED);
 
-        // The "current" may no longer be valid.  MMS depends on this to send properly.
-        mGsmPhone.updateCurrentCarrierInProvider();
+        if (mPhone instanceof GSMPhone) {
+            // The "current" may no longer be valid.  MMS depends on this to send properly. TBD
+            ((GSMPhone)mPhone).updateCurrentCarrierInProvider();
+        }
 
         // TODO: It'd be nice to only do this if the changed entrie(s)
         // match the current operator.
+        if (DBG) log("onApnChanged: createAllApnList and cleanUpAllConnections");
         createAllApnList();
-        if (mState != State.DISCONNECTING) {
-            cleanUpConnection(isConnected, Phone.REASON_APN_CHANGED);
-            if (!isConnected) {
-                // reset reconnect timer
-                mRetryMgr.resetRetryCount();
-                mReregisterOnReconnectFailure = false;
-                trySetupData(Phone.REASON_APN_CHANGED);
+        cleanUpAllConnections(isConnected, Phone.REASON_APN_CHANGED);
+        if (!isConnected) {
+            setupDataOnReadyApns(Phone.REASON_APN_CHANGED);
+        }
+    }
+
+    /**
+     * @param cid Connection id provided from RIL.
+     * @return DataConnectionAc associated with specified cid.
+     */
+    private DataConnectionAc findDataConnectionAcByCid(int cid) {
+        for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
+            if (dcac.getCidSync() == cid) {
+                return dcac;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @param dcacs Collection of DataConnectionAc reported from RIL.
+     * @return List of ApnContext whihc is connected, but does not present in
+     *         data connection list reported from RIL.
+     */
+    private List<ApnContext> findApnContextToClean(Collection<DataConnectionAc> dcacs) {
+        if (dcacs == null) return null;
+
+        ArrayList<ApnContext> list = new ArrayList<ApnContext>();
+        for (ApnContext apnContext : mApnContexts.values()) {
+            if (apnContext.getState() == State.CONNECTED) {
+                boolean found = false;
+                for (DataConnectionAc dcac : dcacs) {
+                    if (dcac == apnContext.getDataConnectionAc()) {
+                        // ApnContext holds the ref to dcac present in data call list.
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found) {
+                    // ApnContext does not have dcac reported in data call list.
+                    // Fetch all the ApnContexts that map to this dcac which are in
+                    // INITING state too.
+                    if (DBG) log("onDataStateChanged(ar): Connected apn not found in the list (" +
+                                 apnContext.toString() + ")");
+                    if (apnContext.getDataConnectionAc() != null) {
+                        list.addAll(apnContext.getDataConnectionAc().getApnListSync());
+                    } else {
+                        list.add(apnContext);
+                    }
+                }
             }
         }
+        return list;
     }
 
     /**
-     * @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
-     * previous state
+     * @param ar is the result of RIL_REQUEST_DATA_CALL_LIST
+     * or RIL_UNSOL_DATA_CALL_LIST_CHANGED
      */
-    private void onDataStateChanged (AsyncResult ar, boolean explicitPoll) {
+    private void onDataStateChanged (AsyncResult ar) {
         ArrayList<DataCallState> dataCallStates;
 
+        if (DBG) log("onDataStateChanged(ar): E");
         dataCallStates = (ArrayList<DataCallState>)(ar.result);
 
         if (ar.exception != null) {
             // This is probably "radio not available" or something
             // of that sort. If so, the whole connection is going
             // to come down soon anyway
+            if (DBG) log("onDataStateChanged(ar): exception; likely radio not available, ignore");
             return;
         }
+        if (DBG) log("onDataStateChanged(ar): DataCallState size=" + dataCallStates.size());
 
-        if (mState == State.CONNECTED) {
-            // The way things are supposed to work, the PDP list
-            // should not contain the CID after it disconnects.
-            // However, the way things really work, sometimes the PDP
-            // context is still listed with active = false, which
-            // makes it hard to distinguish an activating context from
-            // an activated-and-then deactivated one.
-            if (!dataCallStatesHasCID(dataCallStates, mCidActive)) {
-                // It looks like the PDP context has deactivated.
-                // Tear everything down and try to reconnect.
+        // Create a hash map to store the dataCallState of each DataConnectionAc
+        // TODO: Depends on how frequent the DATA_CALL_LIST got updated,
+        //       may cache response to reduce comparison.
+        HashMap<DataCallState, DataConnectionAc> response;
+        response = new HashMap<DataCallState, DataConnectionAc>();
+        for (DataCallState dataCallState : dataCallStates) {
+            DataConnectionAc dcac = findDataConnectionAcByCid(dataCallState.cid);
 
-                log("PDP connection has dropped. Reconnecting");
+            if (dcac != null) response.put(dataCallState, dcac);
+        }
 
-                // Add an event log when the network drops PDP
-                GsmCellLocation loc = ((GsmCellLocation)mPhone.getCellLocation());
-                EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP,
-                        loc != null ? loc.getCid() : -1,
-                        TelephonyManager.getDefault().getNetworkType());
+        // step1: Find a list of "connected" APN which does not have reference to
+        //        calls listed in the Data Call List.
+        List<ApnContext> apnsToClear = findApnContextToClean(response.values());
 
-                cleanUpConnection(true, null);
-                return;
-            } else if (!dataCallStatesHasActiveCID(dataCallStates, mCidActive)) {
-                // Here, we only consider this authoritative if we asked for the
-                // PDP list. If it was an unsolicited response, we poll again
-                // to make sure everyone agrees on the initial state.
-
-                if (!explicitPoll) {
-                    // We think it disconnected but aren't sure...poll from our side
-                    mPhone.mCM.getPDPContextList(
-                            this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE));
-                } else {
-                    log("PDP connection has dropped (active=false case). "
-                                    + " Reconnecting");
+        // step2: Check status of each calls in Data Call List.
+        //        Collect list of ApnContext associated with the data call if the link
+        //        has to be cleared.
+        for (DataCallState newState : dataCallStates) {
+            DataConnectionAc dcac = response.get(newState);
+
+            // no associated DataConnection found. Ignore.
+            if (dcac == null) continue;
+
+            Collection<ApnContext> apns = dcac.getApnListSync();
+
+            // filter out ApnContext with "Connected/Connecting" state.
+            ArrayList<ApnContext> connectedApns = new ArrayList<ApnContext>();
+            for (ApnContext apnContext : apns) {
+                if (apnContext.getState() == State.CONNECTED ||
+                       apnContext.getState() == State.CONNECTING ||
+                       apnContext.getState() == State.INITING) {
+                    connectedApns.add(apnContext);
+                }
+            }
 
-                    // Log the network drop on the event log.
-                    GsmCellLocation loc = ((GsmCellLocation)mPhone.getCellLocation());
-                    EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP,
-                            loc != null ? loc.getCid() : -1,
-                            TelephonyManager.getDefault().getNetworkType());
+            // No "Connected" ApnContext associated with this CID. Ignore.
+            if (connectedApns.isEmpty()) {
+                continue;
+            }
 
-                    cleanUpConnection(true, null);
+            if (DBG) log("onDataStateChanged(ar): Found ConnId=" + newState.cid
+                            + " newState=" + newState.toString());
+            if (newState.active != 0) {
+                boolean resetConnection;
+                switch (dcac.updateLinkPropertiesDataCallStateSync(newState)) {
+                case NONE:
+                    if (DBG) log("onDataStateChanged(ar): Found but no change, skip");
+                    resetConnection = false;
+                    break;
+                case CHANGED:
+                    for (ApnContext apnContext : connectedApns) {
+                        if (DBG) log("onDataStateChanged(ar): Found and changed, notify (" +
+                                     apnContext.toString() + ")");
+                        mPhone.notifyDataConnection(Phone.REASON_LINK_PROPERTIES_CHANGED,
+                                                    apnContext.getApnType());
+                    }
+                    // Temporary hack, at this time a transition from CDMA -> Global
+                    // fails so we'll hope for the best and not reset the connection.
+                    // @see bug/4455071
+                    if (SystemProperties.getBoolean("telephony.ignore-state-changes",
+                                                    true)) {
+                        log("onDataStateChanged(ar): STOPSHIP don't reset, continue");
+                        resetConnection = false;
+                    } else {
+                        // Things changed so reset connection, when hack is removed
+                        // this is the normal path.
+                        log("onDataStateChanged(ar): changed so resetting connection");
+                        resetConnection = true;
+                    }
+                    break;
+                case RESET:
+                default:
+                    if (DBG) log("onDataStateChanged(ar): an error, reset connection");
+                    resetConnection = true;
+                    break;
                 }
+                if (resetConnection == false) continue;
             }
+
+            if (DBG) log("onDataStateChanged(ar): reset connection.");
+
+            apnsToClear.addAll(connectedApns);
         }
+
+        // step3: Clear apn connection if applicable.
+        if (!apnsToClear.isEmpty()) {
+            // Add an event log when the network drops PDP
+            int cid = getCellLocationId();
+            EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP, cid,
+                                TelephonyManager.getDefault().getNetworkType());
+        }
+
+        for (ApnContext apnContext : apnsToClear) {
+            cleanUpConnection(true, apnContext);
+        }
+        if (DBG) log("onDataStateChanged(ar): X");
     }
 
-    private void notifyDefaultData(String reason) {
-        setState(State.CONNECTED);
-        notifyDataConnection(reason);
+    private void notifyDefaultData(ApnContext apnContext) {
+        if (DBG) {
+            log("notifyDefaultData: type=" + apnContext.getApnType()
+                + ", reason:" + apnContext.getReason());
+        }
+        apnContext.setState(State.CONNECTED);
+        // setState(State.CONNECTED);
+        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
         startNetStatPoll();
         // reset reconnect timer
-        mRetryMgr.resetRetryCount();
-        mReregisterOnReconnectFailure = false;
+        apnContext.getDataConnection().resetRetryCount();
     }
 
-    private void gotoIdleAndNotifyDataConnection(String reason) {
+    // TODO: For multiple Active APNs not exactly sure how to do this.
+    protected void gotoIdleAndNotifyDataConnection(String reason) {
         if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason);
-        setState(State.IDLE);
         notifyDataConnection(reason);
         mActiveApn = null;
     }
@@ -608,28 +1218,60 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
     }
 
     private void doRecovery() {
-        if (mState == State.CONNECTED) {
+        if (getOverallState() == State.CONNECTED) {
             int maxPdpReset = Settings.Secure.getInt(mResolver,
                     Settings.Secure.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT,
                     DEFAULT_MAX_PDP_RESET_FAIL);
             if (mPdpResetCount < maxPdpReset) {
                 mPdpResetCount++;
                 EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, mSentSinceLastRecv);
-                cleanUpConnection(true, Phone.REASON_PDP_RESET);
+                if (DBG) log("doRecovery() cleanup all connections mPdpResetCount < max");
+                cleanUpAllConnections(true, Phone.REASON_PDP_RESET);
             } else {
                 mPdpResetCount = 0;
-                EventLog.writeEvent(EventLogTags.PDP_REREGISTER_NETWORK, mSentSinceLastRecv);
-                mGsmPhone.mSST.reRegisterNetwork(null);
+                switch (mRecoveryAction) {
+                case REREGISTER:
+                    EventLog.writeEvent(EventLogTags.PDP_REREGISTER_NETWORK, mSentSinceLastRecv);
+                    if (DBG) log("doRecovery() re-register getting preferred network type");
+                    mPhone.getServiceStateTracker().reRegisterNetwork(null);
+                    mRecoveryAction = RecoveryAction.RADIO_RESTART;
+                    break;
+                case RADIO_RESTART:
+                    EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, mSentSinceLastRecv);
+                    if (DBG) log("restarting radio");
+                    mRecoveryAction = RecoveryAction.RADIO_RESET;
+                    restartRadio();
+                    break;
+                case RADIO_RESET:
+                    // This is in case radio restart has not recovered the data.
+                    // It will set an additional "gsm.radioreset" property to tell
+                    // RIL or system to take further action.
+                    // The implementation of hard reset recovery action is up to OEM product.
+                    // Once gsm.radioreset property is consumed, it is expected to set back
+                    // to false by RIL.
+                    EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, -1);
+                    if (DBG) log("restarting radio with reset indication");
+                    SystemProperties.set("gsm.radioreset", "true");
+                    // give 1 sec so property change can be notified.
+                    try {
+                        Thread.sleep(1000);
+                    } catch (InterruptedException e) {}
+                    restartRadio();
+                    break;
+                default:
+                    throw new RuntimeException("doRecovery: Invalid mRecoveryAction " +
+                        mRecoveryAction);
+                }
             }
-            // TODO: Add increasingly drastic recovery steps, eg,
-            // reset the radio, reset the device.
+        } else {
+            if (DBG) log("doRecovery(): ignore, we're not connected");
         }
     }
 
     @Override
     protected void startNetStatPoll() {
-        if (mState == State.CONNECTED && mNetStatPollEnabled == false) {
-            log("[DataConnection] Start poll NetStat");
+        if (getOverallState() == State.CONNECTED && mNetStatPollEnabled == false) {
+            if (DBG) log("startNetStatPoll");
             resetPollStats();
             mNetStatPollEnabled = true;
             mPollNetStat.run();
@@ -640,14 +1282,14 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
     protected void stopNetStatPoll() {
         mNetStatPollEnabled = false;
         removeCallbacks(mPollNetStat);
-        log("[DataConnection] Stop poll NetStat");
+        if (DBG) log("stopNetStatPoll");
     }
 
     @Override
     protected void restartRadio() {
-        log("************TURN OFF RADIO**************");
-        cleanUpConnection(true, Phone.REASON_RADIO_TURNED_OFF);
-        mGsmPhone.mSST.powerOffRadioSafely();
+        if (DBG) log("restartRadio: ************TURN OFF RADIO**************");
+        cleanUpAllConnections(true, Phone.REASON_RADIO_TURNED_OFF);
+        mPhone.getServiceStateTracker().powerOffRadioSafely(this);
         /* Note: no need to call setRadioPower(true).  Assuming the desired
          * radio power state is still ON (as tracked by ServiceStateTracker),
          * ServiceStateTracker will call setRadioPower when it receives the
@@ -672,10 +1314,30 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
             preTxPkts = mTxPkts;
             preRxPkts = mRxPkts;
 
-            mTxPkts = TrafficStats.getMobileTxPackets();
-            mRxPkts = TrafficStats.getMobileRxPackets();
+            long txSum = 0, rxSum = 0;
+            for (ApnContext apnContext : mApnContexts.values()) {
+                if (apnContext.getState() == State.CONNECTED) {
+                    DataConnectionAc dcac = apnContext.getDataConnectionAc();
+                    if (dcac == null) continue;
+
+                    LinkProperties linkProp = dcac.getLinkPropertiesSync();
+                    if (linkProp == null) continue;
+
+                    String iface = linkProp.getInterfaceName();
 
-            //log("rx " + String.valueOf(rxPkts) + " tx " + String.valueOf(txPkts));
+                    if (iface != null) {
+                        long stats = TrafficStats.getTxPackets(iface);
+                        if (stats > 0) txSum += stats;
+                        stats = TrafficStats.getRxPackets(iface);
+                        if (stats > 0) rxSum += stats;
+                    }
+                }
+            }
+
+            mTxPkts = txSum;
+            mRxPkts = rxSum;
+
+            // log("tx " + mTxPkts + " rx " + mRxPkts);
 
             if (mNetStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) {
                 sent = mTxPkts - preTxPkts;
@@ -685,6 +1347,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
                     mSentSinceLastRecv = 0;
                     newActivity = Activity.DATAINANDOUT;
                     mPdpResetCount = 0;
+                    mRecoveryAction = RecoveryAction.REREGISTER;
                 } else if (sent > 0 && received == 0) {
                     if (mPhone.getState() == Phone.State.IDLE) {
                         mSentSinceLastRecv += sent;
@@ -696,6 +1359,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
                     mSentSinceLastRecv = 0;
                     newActivity = Activity.DATAIN;
                     mPdpResetCount = 0;
+                    mRecoveryAction = RecoveryAction.REREGISTER;
                 } else if (sent == 0 && received == 0) {
                     newActivity = Activity.NONE;
                 } else {
@@ -726,8 +1390,8 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
                 if (mNoRecvPollCount < noRecvPollLimit) {
                     // It's possible the PDP context went down and we weren't notified.
                     // Start polling the context list in an attempt to recover.
-                    if (DBG) log("no DATAIN in a while; polling PDP");
-                    mPhone.mCM.getDataCallList(obtainMessage(EVENT_GET_PDP_LIST_COMPLETE));
+                    if (DBG) log("Polling: no DATAIN in a while; polling PDP");
+                    mPhone.mCM.getDataCallList(obtainMessage(EVENT_DATA_STATE_CHANGED));
 
                     mNoRecvPollCount++;
 
@@ -736,9 +1400,9 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
                             Settings.Secure.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS,
                             POLL_NETSTAT_SLOW_MILLIS);
                 } else {
-                    if (DBG) log("Sent " + String.valueOf(mSentSinceLastRecv) +
+                    if (DBG) log("Polling: Sent " + String.valueOf(mSentSinceLastRecv) +
                                         " pkts since last received start recovery process");
-                    stopNetStatPoll();
+                    mNoRecvPollCount = 0;
                     sendMessage(obtainMessage(EVENT_START_RECOVERY));
                 }
             } else {
@@ -784,122 +1448,244 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
         return retry;
     }
 
-    private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) {
-        if (mState == State.FAILED) {
-            /** TODO: Retrieve retry manager from connection itself */
-            if (!mRetryMgr.isRetryNeeded()) {
-                if (!mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
-                    // if no more retries on a secondary APN attempt, tell the world and revert.
-                    notifyDataConnection(Phone.REASON_APN_FAILED);
-                    onEnableApn(apnTypeToId(mRequestedApnType), DISABLED);
+    private void reconnectAfterFail(FailCause lastFailCauseCode,
+                                    ApnContext apnContext, int retryOverride) {
+        if (apnContext == null) {
+            loge("reconnectAfterFail: apnContext == null, impossible");
+            return;
+        }
+        if ((apnContext.getState() == State.FAILED) &&
+            (apnContext.getDataConnection() != null)) {
+            if (!apnContext.getDataConnection().isRetryNeeded()) {
+                if (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT)) {
+                    mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType());
                     return;
                 }
                 if (mReregisterOnReconnectFailure) {
-                    // We've re-registered once now just retry forever.
-                    mRetryMgr.retryForeverUsingLastTimeout();
+                    // We've re-registerd once now just retry forever.
+                    apnContext.getDataConnection().retryForeverUsingLastTimeout();
                 } else {
-                    // Try to re-register to the network.
-                    log("PDP activate failed, Reregistering to the network");
+                    // Try to Re-register to the network.
+                    if (DBG) log("reconnectAfterFail: activate failed, Reregistering to network");
                     mReregisterOnReconnectFailure = true;
-                    mGsmPhone.mSST.reRegisterNetwork(null);
-                    mRetryMgr.resetRetryCount();
+                    mPhone.getServiceStateTracker().reRegisterNetwork(null);
+                    apnContext.getDataConnection().resetRetryCount();
                     return;
                 }
             }
 
-            int nextReconnectDelay = mRetryMgr.getRetryTimer();
-            log("PDP activate failed. Scheduling next attempt for "
-                    + (nextReconnectDelay / 1000) + "s");
-
-            AlarmManager am =
-                (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
-            Intent intent = new Intent(INTENT_RECONNECT_ALARM);
-            intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason);
-            mReconnectIntent = PendingIntent.getBroadcast(
-                    mPhone.getContext(), 0, intent, 0);
-            am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                    SystemClock.elapsedRealtime() + nextReconnectDelay,
-                    mReconnectIntent);
-
-            mRetryMgr.increaseRetryCount();
+            // If retry needs to be backed off for specific case (determined by RIL/Modem)
+            // use the specified timer instead of pre-configured retry pattern.
+            int nextReconnectDelay = retryOverride;
+            if (nextReconnectDelay < 0) {
+                nextReconnectDelay = apnContext.getDataConnection().getRetryTimer();
+                apnContext.getDataConnection().increaseRetryCount();
+            }
+            startAlarmForReconnect(nextReconnectDelay, apnContext);
 
             if (!shouldPostNotification(lastFailCauseCode)) {
-                log("NOT Posting GPRS Unavailable notification "
+                if (DBG) {
+                    log("reconnectAfterFail: NOT Posting GPRS Unavailable notification "
                                 + "-- likely transient error");
+                }
             } else {
-                notifyNoData(lastFailCauseCode);
+                notifyNoData(lastFailCauseCode, apnContext);
             }
         }
     }
 
-    private void notifyNoData(GsmDataConnection.FailCause lastFailCauseCode) {
-        setState(State.FAILED);
+    private void startAlarmForReconnect(int delay, ApnContext apnContext) {
+
+        if (DBG) {
+            log("Schedule alarm for reconnect: activate failed. Scheduling next attempt for "
+                + (delay / 1000) + "s");
+        }
+
+        DataConnectionAc dcac = apnContext.getDataConnectionAc();
+
+        if ((dcac == null) || (dcac.dataConnection == null)) {
+            // should not happen, but just in case.
+            loge("null dcac or dc.");
+            return;
+        }
+
+        AlarmManager am =
+            (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
+
+        Intent intent = new Intent(INTENT_RECONNECT_ALARM + '.' +
+                                   dcac.dataConnection.getDataConnectionId());
+        intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, apnContext.getReason());
+        intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE,
+                        dcac.dataConnection.getDataConnectionId());
+
+        PendingIntent alarmIntent = PendingIntent.getBroadcast (mPhone.getContext(), 0,
+                                                                intent, 0);
+        dcac.setReconnectIntentSync(alarmIntent);
+        am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                SystemClock.elapsedRealtime() + delay, alarmIntent);
+
+    }
+
+    private void notifyNoData(GsmDataConnection.FailCause lastFailCauseCode,
+                              ApnContext apnContext) {
+        if (DBG) log( "notifyNoData: type=" + apnContext.getApnType());
+        apnContext.setState(State.FAILED);
+        if (lastFailCauseCode.isPermanentFail()
+            && (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT))) {
+            mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType());
+        }
     }
 
     private void onRecordsLoaded() {
+        if (DBG) log("onRecordsLoaded: createAllApnList");
         createAllApnList();
-        if (mState == State.FAILED) {
-            cleanUpConnection(false, null);
+        if (mRadioAvailable) {
+            if (DBG) log("onRecordsLoaded: notifying data availability");
+            notifyDataAvailability(Phone.REASON_SIM_LOADED);
         }
-        sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, Phone.REASON_SIM_LOADED));
+        setupDataOnReadyApns(Phone.REASON_SIM_LOADED);
     }
 
     @Override
-    protected void onEnableNewApn() {
-        log("onEnableNewApn E");
-        // change our retry manager to use the appropriate numbers for the new APN
-        if (mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
-            log("onEnableNewApn default type");
-            mRetryMgr = mPendingDataConnection.getRetryMgr();
-            mRetryMgr.resetRetryCount();
-        } else if (mApnToDataConnectionId.get(mRequestedApnType) == null) {
-            log("onEnableNewApn mRequestedApnType=" + mRequestedApnType +
-                    " missing, make a new connection");
-            int id = createDataConnection(mRequestedApnType);
-            mRetryMgr = mDataConnections.get(id).getRetryMgr();
-            mRetryMgr.resetRetryCount();
+    protected void onSetDependencyMet(String apnType, boolean met) {
+        // don't allow users to tweak hipri to work around default dependency not met
+        if (Phone.APN_TYPE_HIPRI.equals(apnType)) return;
+
+        ApnContext apnContext = mApnContexts.get(apnType);
+        if (apnContext == null) {
+            loge("onSetDependencyMet: ApnContext not found in onSetDependencyMet(" +
+                    apnType + ", " + met + ")");
+            return;
+        }
+        applyNewState(apnContext, apnContext.isEnabled(), met);
+        if (Phone.APN_TYPE_DEFAULT.equals(apnType)) {
+            // tie actions on default to similar actions on HIPRI regarding dependencyMet
+            apnContext = mApnContexts.get(Phone.APN_TYPE_HIPRI);
+            if (apnContext != null) applyNewState(apnContext, apnContext.isEnabled(), met);
+        }
+    }
+
+    private void applyNewState(ApnContext apnContext, boolean enabled, boolean met) {
+        boolean cleanup = false;
+        boolean trySetup = false;
+        if (DBG) {
+            log("applyNewState(" + apnContext.getApnType() + ", " + enabled +
+                    "(" + apnContext.isEnabled() + "), " + met + "(" +
+                    apnContext.getDependencyMet() +"))");
+        }
+        if (apnContext.isReady()) {
+            if (enabled && met) return;
+            if (!enabled) {
+                apnContext.setReason(Phone.REASON_DATA_DISABLED);
+            } else {
+                apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_UNMET);
+            }
+            cleanup = true;
         } else {
-            log("oneEnableNewApn connection already exists, nothing to setup");
+            if (enabled && met) {
+                if (apnContext.isEnabled()) {
+                    apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_MET);
+                } else {
+                    apnContext.setReason(Phone.REASON_DATA_ENABLED);
+                }
+                if (apnContext.getState() == State.FAILED) {
+                    apnContext.setState(State.IDLE);
+                }
+                trySetup = true;
+            }
+        }
+        apnContext.setEnabled(enabled);
+        apnContext.setDependencyMet(met);
+        if (cleanup) cleanUpConnection(true, apnContext);
+        if (trySetup) trySetupData(apnContext);
+    }
+
+    private DataConnection checkForConnectionForApnContext(ApnContext apnContext) {
+        // Loop through all apnContexts looking for one with a conn that satisfies this apnType
+        String apnType = apnContext.getApnType();
+        for (ApnContext c : mApnContexts.values()) {
+            DataConnection conn = c.getDataConnection();
+            if (conn != null) {
+                ApnSetting apnSetting = c.getApnSetting();
+                if (apnSetting != null && apnSetting.canHandleType(apnType)) {
+                    if (DBG) {
+                        log("checkForConnectionForApnContext: apnContext=" + apnContext +
+                                " found conn=" + conn);
+                    }
+                    return conn;
+                }
+            }
         }
+        if (DBG) log("checkForConnectionForApnContext: apnContext=" + apnContext + " NO conn");
+        return null;
+    }
 
-        // TODO:  To support simultaneous PDP contexts, this should really only call
-        // cleanUpConnection if it needs to free up a GsmDataConnection.
-        cleanUpConnection(true, Phone.REASON_APN_SWITCHED);
-        log("onEnableNewApn X");
+    @Override
+    protected void onEnableApn(int apnId, int enabled) {
+        ApnContext apnContext = mApnContexts.get(apnIdToType(apnId));
+        if (apnContext == null) {
+            loge("onEnableApn(" + apnId + ", " + enabled + "): NO ApnContext");
+            return;
+        }
+        // TODO change our retry manager to use the appropriate numbers for the new APN
+        if (DBG) log("onEnableApn: apnContext=" + apnContext + " call applyNewState");
+        applyNewState(apnContext, enabled == ENABLED, apnContext.getDependencyMet());
     }
 
     @Override
+    // TODO: We shouldnt need this.
     protected boolean onTrySetupData(String reason) {
-        return trySetupData(reason);
+        if (DBG) log("onTrySetupData: reason=" + reason);
+        setupDataOnReadyApns(reason);
+        return true;
+    }
+
+    protected boolean onTrySetupData(ApnContext apnContext) {
+        if (DBG) log("onTrySetupData: apnContext=" + apnContext);
+        return trySetupData(apnContext);
     }
 
     @Override
     protected void onRoamingOff() {
-        trySetupData(Phone.REASON_ROAMING_OFF);
+        if (DBG) log("onRoamingOff");
+        // Notify data availability so APN can be enabled.
+        notifyDataAvailability(Phone.REASON_ROAMING_OFF);
+
+        setupDataOnReadyApns(Phone.REASON_ROAMING_OFF);
     }
 
     @Override
     protected void onRoamingOn() {
+        // Notify data availability so APN can be enabled.
+        notifyDataAvailability(Phone.REASON_ROAMING_ON);
+
         if (getDataOnRoamingEnabled()) {
-            trySetupData(Phone.REASON_ROAMING_ON);
+            if (DBG) log("onRoamingOn: setup data on roaming");
+            setupDataOnReadyApns(Phone.REASON_ROAMING_ON);
         } else {
-            if (DBG) log("Tear down data connection on roaming.");
-            cleanUpConnection(true, Phone.REASON_ROAMING_ON);
+            if (DBG) log("onRoamingOn: Tear down data connection on roaming.");
+            cleanUpAllConnections(true, Phone.REASON_ROAMING_ON);
         }
     }
 
     @Override
     protected void onRadioAvailable() {
+        if (DBG) log("onRadioAvailable");
+        mRadioAvailable = true;
         if (mPhone.getSimulatedRadioControl() != null) {
             // Assume data is connected on the simulator
             // FIXME  this can be improved
-            setState(State.CONNECTED);
+            // setState(State.CONNECTED);
             notifyDataConnection(null);
 
-            log("We're on the simulator; assuming data is connected");
+            log("onRadioAvailable: We're on the simulator; assuming data is connected");
+        }
+
+        if (mPhone.mIccRecords.getRecordsLoaded()) {
+            notifyDataAvailability(null);
         }
 
-        if (mState != State.IDLE) {
+        if (getOverallState() != State.IDLE) {
             cleanUpConnection(true, null);
         }
     }
@@ -908,72 +1694,80 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
     protected void onRadioOffOrNotAvailable() {
         // Make sure our reconnect delay starts at the initial value
         // next time the radio comes on
-        mRetryMgr.resetRetryCount();
+
+        for (DataConnection dc : mDataConnections.values()) {
+            dc.resetRetryCount();
+        }
         mReregisterOnReconnectFailure = false;
+        mRadioAvailable = false;
 
         if (mPhone.getSimulatedRadioControl() != null) {
             // Assume data is connected on the simulator
             // FIXME  this can be improved
             log("We're on the simulator; assuming radio off is meaningless");
         } else {
-            if (DBG) log("Radio is off and clean up all connection");
-            // TODO: Should we reset mRequestedApnType to "default"?
-            cleanUpConnection(false, Phone.REASON_RADIO_TURNED_OFF);
+            if (DBG) log("onRadioOffOrNotAvailable: is off and clean up all connections");
+            cleanUpAllConnections(false, Phone.REASON_RADIO_TURNED_OFF);
         }
+        notifyDataAvailability(null);
     }
 
     @Override
     protected void onDataSetupComplete(AsyncResult ar) {
-        /** TODO: Which connection is completing should be a parameter */
-        String reason = null;
-        if (ar.userObj instanceof String) {
-            reason = (String) ar.userObj;
+
+        ApnContext apnContext = null;
+
+        if(ar.userObj instanceof ApnContext){
+            apnContext = (ApnContext)ar.userObj;
+        } else {
+            throw new RuntimeException("onDataSetupComplete: No apnContext");
         }
 
-        if (ar.exception == null) {
-            if(DBG) {
-                log(String.format("onDataSetupComplete: success apn=%s", mWaitingApns.get(0).apn));
+        if (isDataSetupCompleteOk(ar)) {
+            DataConnectionAc dcac = apnContext.getDataConnectionAc();
+            if (dcac == null) {
+                throw new RuntimeException("onDataSetupCompete: No dcac");
             }
-            // TODO: We should clear LinkProperties/Capabilities when torn down or disconnected
-            mLinkProperties = getLinkProperties(mPendingDataConnection);
-            mLinkCapabilities = getLinkCapabilities(mPendingDataConnection);
+            DataConnection dc = apnContext.getDataConnection();
 
-            ApnSetting apn = mPendingDataConnection.getApn();
+            if (DBG) {
+                log(String.format("onDataSetupComplete: success apn=%s",
+                    apnContext.getWaitingApns().get(0).apn));
+            }
+            ApnSetting apn = apnContext.getApnSetting();
             if (apn.proxy != null && apn.proxy.length() != 0) {
                 try {
                     ProxyProperties proxy = new ProxyProperties(apn.proxy,
                             Integer.parseInt(apn.port), null);
-                    mLinkProperties.setHttpProxy(proxy);
+                    dcac.setLinkPropertiesHttpProxySync(proxy);
                 } catch (NumberFormatException e) {
-                    loge("NumberFormatException making ProxyProperties (" + apn.port +
-                            "): " + e);
+                    loge("onDataSetupComplete: NumberFormatException making ProxyProperties (" +
+                            apn.port + "): " + e);
                 }
             }
 
             // everything is setup
-            if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) {
+            if(TextUtils.equals(apnContext.getApnType(),Phone.APN_TYPE_DEFAULT)) {
                 SystemProperties.set("gsm.defaultpdpcontext.active", "true");
-                        if (canSetPreferApn && mPreferredApn == null) {
-                            log("PREFERRED APN is null");
-                            mPreferredApn = mActiveApn;
-                            setPreferredApn(mPreferredApn.id);
-                        }
+                if (canSetPreferApn && mPreferredApn == null) {
+                    if (DBG) log("onDataSetupComplete: PREFERED APN is null");
+                    mPreferredApn = apnContext.getApnSetting();
+                    if (mPreferredApn != null) {
+                        setPreferredApn(mPreferredApn.id);
+                    }
+                }
             } else {
                 SystemProperties.set("gsm.defaultpdpcontext.active", "false");
             }
-            notifyDefaultData(reason);
-
-            // TODO: For simultaneous PDP support, we need to build another
-            // trigger another TRY_SETUP_DATA for the next APN type.  (Note
-            // that the existing connection may service that type, in which
-            // case we should try the next type, etc.
+            notifyDefaultData(apnContext);
         } else {
-            GsmDataConnection.FailCause cause;
-            cause = (GsmDataConnection.FailCause) (ar.result);
+            String apnString;
+            DataConnection.FailCause cause;
+
+            cause = (DataConnection.FailCause) (ar.result);
             if (DBG) {
-                String apnString;
                 try {
-                    apnString = mWaitingApns.get(0).apn;
+                    apnString = apnContext.getWaitingApns().get(0).apn;
                 } catch (Exception e) {
                     apnString = "<unknown>";
                 }
@@ -981,35 +1775,52 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
             }
             if (cause.isEventLoggable()) {
                 // Log this failure to the Event Logs.
-                GsmCellLocation loc = ((GsmCellLocation)mPhone.getCellLocation());
+                int cid = getCellLocationId();
                 EventLog.writeEvent(EventLogTags.PDP_SETUP_FAIL,
-                        cause.ordinal(), loc != null ? loc.getCid() : -1,
-                        TelephonyManager.getDefault().getNetworkType());
+                        cause.ordinal(), cid, TelephonyManager.getDefault().getNetworkType());
             }
 
             // Count permanent failures and remove the APN we just tried
-            mWaitingApnsPermanentFailureCountDown -= cause.isPermanentFail() ? 1 : 0;
-            mWaitingApns.remove(0);
-            if (DBG) log(String.format("onDataSetupComplete: mWaitingApns.size=%d" +
-                            " mWaitingApnsPermanenatFailureCountDown=%d",
-                            mWaitingApns.size(), mWaitingApnsPermanentFailureCountDown));
+            if (cause.isPermanentFail()) apnContext.decWaitingApnsPermFailCount();
+
+            apnContext.removeNextWaitingApn();
+            if (DBG) {
+                log(String.format("onDataSetupComplete: WaitingApns.size=%d" +
+                        " WaitingApnsPermFailureCountDown=%d",
+                        apnContext.getWaitingApns().size(),
+                        apnContext.getWaitingApnsPermFailCount()));
+            }
 
             // See if there are more APN's to try
-            if (mWaitingApns.isEmpty()) {
-                if (mWaitingApnsPermanentFailureCountDown == 0) {
-                    if (DBG) log("onDataSetupComplete: Permanent failures stop retrying");
-                    notifyNoData(cause);
-                    notifyDataConnection(Phone.REASON_APN_FAILED);
+            if (apnContext.getWaitingApns().isEmpty()) {
+                if (apnContext.getWaitingApnsPermFailCount() == 0) {
+                    if (DBG) {
+                        log("onDataSetupComplete: All APN's had permanent failures, stop retrying");
+                    }
+                    apnContext.setState(State.FAILED);
+                    mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType());
+
+                    apnContext.setDataConnection(null);
+                    apnContext.setDataConnectionAc(null);
+                    if (DBG) {
+                        log("onDataSetupComplete: permanent error apn=%s" + apnString );
+                    }
                 } else {
                     if (DBG) log("onDataSetupComplete: Not all permanent failures, retry");
-                    startDelayedRetry(cause, reason);
+                    // check to see if retry should be overridden for this failure.
+                    int retryOverride = -1;
+                    if (ar.exception instanceof DataConnection.CallSetupException) {
+                        retryOverride =
+                            ((DataConnection.CallSetupException)ar.exception).getRetryOverride();
+                    }
+                    startDelayedRetry(cause, apnContext, retryOverride);
                 }
             } else {
                 if (DBG) log("onDataSetupComplete: Try next APN");
-                setState(State.SCANNING);
+                apnContext.setState(State.SCANNING);
                 // Wait a bit before trying the next APN, so that
                 // we're not tying up the RIL command channel
-                sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, reason), APN_DELAY_MILLIS);
+                startAlarmForReconnect(APN_DELAY_MILLIS, apnContext);
             }
         }
     }
@@ -1019,43 +1830,59 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
      */
     @Override
     protected void onDisconnectDone(int connId, AsyncResult ar) {
-        if(DBG) log("EVENT_DISCONNECT_DONE connId=" + connId);
-        String reason = null;
-        if (ar.userObj instanceof String) {
-           reason = (String) ar.userObj;
+        ApnContext apnContext = null;
+
+        if(DBG) log("onDisconnectDone: EVENT_DISCONNECT_DONE connId=" + connId);
+        if (ar.userObj instanceof ApnContext) {
+            apnContext = (ApnContext) ar.userObj;
+        } else {
+            loge("Invalid ar in onDisconnectDone");
+            return;
         }
-        setState(State.IDLE);
-        notifyDataConnection(reason);
-        mActiveApn = null;
-        if (retryAfterDisconnected(reason)) {
-            trySetupData(reason);
+
+        apnContext.setState(State.IDLE);
+
+        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
+
+        // if all data connection are gone, check whether Airplane mode request was
+        // pending.
+        if (!isConnected()) {
+            if (mPhone.getServiceStateTracker().processPendingRadioPowerOffAfterDataOff()) {
+                // Radio will be turned off. No need to retry data setup
+                apnContext.setApnSetting(null);
+                apnContext.setDataConnection(null);
+                apnContext.setDataConnectionAc(null);
+                return;
+            }
         }
-    }
 
-    /**
-     * Called when EVENT_RESET_DONE is received.
-     */
-    @Override
-    protected void onResetDone(AsyncResult ar) {
-        if (DBG) log("EVENT_RESET_DONE");
-        String reason = null;
-        if (ar.userObj instanceof String) {
-            reason = (String) ar.userObj;
+        // If APN is still enabled, try to bring it back up automatically
+        if (apnContext.isReady() && retryAfterDisconnected(apnContext.getReason())) {
+            SystemProperties.set("gsm.defaultpdpcontext.active", "false");  // TODO - what the heck?  This shoudld go
+            // Wait a bit before trying the next APN, so that
+            // we're not tying up the RIL command channel.
+            // This also helps in any external dependency to turn off the context.
+            startAlarmForReconnect(APN_DELAY_MILLIS, apnContext);
+        } else {
+            apnContext.setApnSetting(null);
+            apnContext.setDataConnection(null);
+            apnContext.setDataConnectionAc(null);
         }
-        gotoIdleAndNotifyDataConnection(reason);
     }
 
     protected void onPollPdp() {
-        if (mState == State.CONNECTED) {
+        if (getOverallState() == State.CONNECTED) {
             // only poll when connected
-            mPhone.mCM.getPDPContextList(this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE));
+            mPhone.mCM.getDataCallList(this.obtainMessage(EVENT_DATA_STATE_CHANGED));
             sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), POLL_PDP_MILLIS);
         }
     }
 
     @Override
     protected void onVoiceCallStarted() {
-        if (mState == State.CONNECTED && ! mGsmPhone.mSST.isConcurrentVoiceAndData()) {
+        if (DBG) log("onVoiceCallStarted");
+        if (isConnected() && ! mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
+            if (DBG) log("onVoiceCallStarted stop polling");
             stopNetStatPoll();
             notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
         }
@@ -1063,8 +1890,9 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
 
     @Override
     protected void onVoiceCallEnded() {
-        if (mState == State.CONNECTED) {
-            if (!mGsmPhone.mSST.isConcurrentVoiceAndData()) {
+        if (DBG) log("onVoiceCallEnded");
+        if (isConnected()) {
+            if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
                 startNetStatPoll();
                 notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
             } else {
@@ -1073,16 +1901,40 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
             }
         } else {
             // reset reconnect timer
-            mRetryMgr.resetRetryCount();
-            mReregisterOnReconnectFailure = false;
-            // in case data setup was attempted when we were on a voice call
-            trySetupData(Phone.REASON_VOICE_CALL_ENDED);
+            setupDataOnReadyApns(Phone.REASON_VOICE_CALL_ENDED);
+        }
+    }
+
+    @Override
+    protected void onCleanUpConnection(boolean tearDown, int apnId, String reason) {
+        if (DBG) log("onCleanUpConnection");
+        ApnContext apnContext = mApnContexts.get(apnIdToType(apnId));
+        if (apnContext != null) {
+            apnContext.setReason(reason);
+            cleanUpConnection(tearDown, apnContext);
         }
     }
 
+    protected boolean isConnected() {
+        for (ApnContext apnContext : mApnContexts.values()) {
+            if (apnContext.getState() == State.CONNECTED) {
+            return true;
+            }
+        }
+        return false;
+    }
+
     @Override
-    protected void onCleanUpConnection(boolean tearDown, String reason) {
-        cleanUpConnection(tearDown, reason);
+    protected void notifyDataConnection(String reason) {
+        if (DBG) log("notifyDataConnection: reason=" + reason);
+        for (ApnContext apnContext : mApnContexts.values()) {
+            if (apnContext.isReady()) {
+                if (DBG) log("notifyDataConnection: type:"+apnContext.getApnType());
+                mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(),
+                        apnContext.getApnType());
+            }
+        }
+        notifyDataAvailability(reason);
     }
 
     /**
@@ -1091,10 +1943,10 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
      */
     private void createAllApnList() {
         mAllApns = new ArrayList<ApnSetting>();
-        String operator = mGsmPhone.mSIMRecords.getSIMOperatorNumeric();
-
+        String operator = mPhone.mIccRecords.getOperatorNumeric();
         if (operator != null) {
             String selection = "numeric = '" + operator + "'";
+            if (DBG) log("createAllApnList: selection=" + selection);
 
             Cursor cursor = mPhone.getContext().getContentResolver().query(
                     Telephony.Carriers.CONTENT_URI, null, selection, null, null);
@@ -1108,73 +1960,80 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
         }
 
         if (mAllApns.isEmpty()) {
-            if (DBG) log("No APN found for carrier: " + operator);
+            if (DBG) log("createAllApnList: No APN found for carrier: " + operator);
             mPreferredApn = null;
-            notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN);
+            // TODO: What is the right behaviour?
+            //notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN);
         } else {
             mPreferredApn = getPreferredApn();
-            log("Get PreferredAPN");
             if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) {
                 mPreferredApn = null;
                 setPreferredApn(-1);
             }
+            if (DBG) log("createAllApnList: mPreferredApn=" + mPreferredApn);
         }
+        if (DBG) log("createAllApnList: X mAllApns=" + mAllApns);
     }
 
     /** Return the id for a new data connection */
-    private int createDataConnection(String apnType) {
-        log("createDataConnection(" + apnType + ") E");
+    private GsmDataConnection createDataConnection() {
+        if (DBG) log("createDataConnection E");
+
         RetryManager rm = new RetryManager();
+        int id = mUniqueIdGenerator.getAndIncrement();
+        GsmDataConnection conn = GsmDataConnection.makeDataConnection(mPhone, id, rm);
+        mDataConnections.put(id, conn);
+        DataConnectionAc dcac = new DataConnectionAc(conn, LOG_TAG);
+        int status = dcac.fullyConnectSync(mPhone.getContext(), this, conn.getHandler());
+        if (status == AsyncChannel.STATUS_SUCCESSFUL) {
+            mDataConnectionAsyncChannels.put(dcac.dataConnection.getDataConnectionId(), dcac);
+        } else {
+            loge("createDataConnection: Could not connect to dcac.mDc=" + dcac.dataConnection +
+                    " status=" + status);
+        }
+
+        // install reconnect intent filter for this data connection.
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(INTENT_RECONNECT_ALARM + '.' + id);
+        mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
+
+        if (DBG) log("createDataConnection() X id=" + id);
+        return conn;
+    }
+
+    private void configureRetry(DataConnection dc, String apnType) {
+        if ((dc == null) || (apnType == null)) return;
 
         if (apnType.equals(Phone.APN_TYPE_DEFAULT)) {
-            if (!rm.configure(SystemProperties.get("ro.gsm.data_retry_config"))) {
-                if (!rm.configure(DEFAULT_DATA_RETRY_CONFIG)) {
+            if (!dc.configureRetry(SystemProperties.get("ro.gsm.data_retry_config"))) {
+                if (!dc.configureRetry(DEFAULT_DATA_RETRY_CONFIG)) {
                     // Should never happen, log an error and default to a simple linear sequence.
-                    log("Could not configure using DEFAULT_DATA_RETRY_CONFIG="
-                            + DEFAULT_DATA_RETRY_CONFIG);
-                    rm.configure(20, 2000, 1000);
+                    loge("configureRetry: Could not configure using " +
+                            "DEFAULT_DATA_RETRY_CONFIG=" + DEFAULT_DATA_RETRY_CONFIG);
+                    dc.configureRetry(20, 2000, 1000);
                 }
             }
         } else {
-            if (!rm.configure(SystemProperties.get("ro.gsm.2nd_data_retry_config"))) {
-                if (!rm.configure(SECONDARY_DATA_RETRY_CONFIG)) {
+            if (!dc.configureRetry(SystemProperties.get("ro.gsm.2nd_data_retry_config"))) {
+                if (!dc.configureRetry(SECONDARY_DATA_RETRY_CONFIG)) {
                     // Should never happen, log an error and default to a simple sequence.
-                    log("Could note configure using SECONDARY_DATA_RETRY_CONFIG="
-                            + SECONDARY_DATA_RETRY_CONFIG);
-                    rm.configure("max_retries=3, 333, 333, 333");
+                    loge("configureRetry: Could note configure using " +
+                            "SECONDARY_DATA_RETRY_CONFIG=" + SECONDARY_DATA_RETRY_CONFIG);
+                    dc.configureRetry("max_retries=3, 333, 333, 333");
                 }
             }
         }
-
-        int id = mUniqueIdGenerator.getAndIncrement();
-        DataConnection conn = GsmDataConnection.makeDataConnection(mGsmPhone, id, rm);
-        mDataConnections.put(id, conn);
-        mApnToDataConnectionId.put(apnType, id);
-
-        log("createDataConnection(" + apnType + ") X id=" + id);
-        return id;
     }
 
     private void destroyDataConnections() {
         if(mDataConnections != null) {
-            log("destroyDataConnectionList clear mDataConnectionList");
+            if (DBG) log("destroyDataConnections: clear mDataConnectionList");
             mDataConnections.clear();
         } else {
-            log("destroyDataConnectionList mDataConnecitonList is empty, ignore");
+            if (DBG) log("destroyDataConnections: mDataConnecitonList is empty, ignore");
         }
     }
 
-    private ApnSetting fetchDunApn() {
-        Context c = mPhone.getContext();
-        String apnData = Settings.Secure.getString(c.getContentResolver(),
-                                    Settings.Secure.TETHER_DUN_APN);
-        ApnSetting dunSetting = ApnSetting.fromString(apnData);
-        if (dunSetting != null) return dunSetting;
-
-        apnData = c.getResources().getString(R.string.config_tether_apndata);
-        return ApnSetting.fromString(apnData);
-    }
-
     /**
      * Build a list of APNs to be used to create PDP's.
      *
@@ -1187,53 +2046,44 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
 
         if (requestedApnType.equals(Phone.APN_TYPE_DUN)) {
             ApnSetting dun = fetchDunApn();
-            if (dun != null) apnList.add(dun);
-            return apnList;
+            if (dun != null) {
+                apnList.add(dun);
+                if (DBG) log("buildWaitingApns: X added APN_TYPE_DUN apnList=" + apnList);
+                return apnList;
+            }
         }
 
-        String operator = mGsmPhone.mSIMRecords.getSIMOperatorNumeric();
-
+        String operator = mPhone.mIccRecords.getOperatorNumeric();
         if (requestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
             if (canSetPreferApn && mPreferredApn != null) {
-                log("Preferred APN:" + operator + ":"
+                if (DBG) {
+                    log("buildWaitingApns: Preferred APN:" + operator + ":"
                         + mPreferredApn.numeric + ":" + mPreferredApn);
+                }
                 if (mPreferredApn.numeric.equals(operator)) {
-                    log("Waiting APN set to preferred APN");
                     apnList.add(mPreferredApn);
+                    if (DBG) log("buildWaitingApns: X added preferred apnList=" + apnList);
                     return apnList;
                 } else {
+                    if (DBG) log("buildWaitingApns: no preferred APN");
                     setPreferredApn(-1);
                     mPreferredApn = null;
                 }
             }
         }
-
         if (mAllApns != null) {
             for (ApnSetting apn : mAllApns) {
                 if (apn.canHandleType(requestedApnType)) {
                     apnList.add(apn);
                 }
             }
+        } else {
+            loge("mAllApns is empty!");
         }
+        if (DBG) log("buildWaitingApns: X apnList=" + apnList);
         return apnList;
     }
 
-    /**
-     * Get next apn in waitingApns
-     * @return the first apn found in waitingApns, null if none
-     */
-    private ApnSetting getNextApn() {
-        ArrayList<ApnSetting> list = mWaitingApns;
-        ApnSetting apn = null;
-
-        if (list != null) {
-            if (!list.isEmpty()) {
-                apn = list.get(0);
-            }
-        }
-        return apn;
-    }
-
     private String apnListToString (ArrayList<ApnSetting> apns) {
         StringBuilder result = new StringBuilder();
         for (int i = 0, size = apns.size(); i < size; i++) {
@@ -1244,9 +2094,10 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
         return result.toString();
     }
 
-    private void startDelayedRetry(GsmDataConnection.FailCause cause, String reason) {
-        notifyNoData(cause);
-        reconnectAfterFail(cause, reason);
+    private void startDelayedRetry(GsmDataConnection.FailCause cause,
+                                   ApnContext apnContext, int retryOverride) {
+        notifyNoData(cause, apnContext);
+        reconnectAfterFail(cause, apnContext, retryOverride);
     }
 
     private void setPreferredApn(int pos) {
@@ -1300,10 +2151,10 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
 
     @Override
     public void handleMessage (Message msg) {
-        if (DBG) log("GSMDataConnTrack handleMessage "+msg);
+        if (DBG) log("handleMessage msg=" + msg);
 
-        if (!mGsmPhone.mIsTheCurrentActivePhone) {
-            log("Ignore GSM msgs since GSM phone is inactive");
+        if (!mPhone.mIsTheCurrentActivePhone || mIsDisposed) {
+            loge("handleMessage: Ignore GSM msgs since GSM phone is inactive");
             return;
         }
 
@@ -1312,20 +2163,16 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
                 onRecordsLoaded();
                 break;
 
-            case EVENT_GPRS_DETACHED:
-                onGprsDetached();
+            case EVENT_DATA_CONNECTION_DETACHED:
+                onDataConnectionDetached();
                 break;
 
-            case EVENT_GPRS_ATTACHED:
-                onGprsAttached();
+            case EVENT_DATA_CONNECTION_ATTACHED:
+                onDataConnectionAttached();
                 break;
 
             case EVENT_DATA_STATE_CHANGED:
-                onDataStateChanged((AsyncResult) msg.obj, false);
-                break;
-
-            case EVENT_GET_PDP_LIST_COMPLETE:
-                onDataStateChanged((AsyncResult) msg.obj, true);
+                onDataStateChanged((AsyncResult) msg.obj);
                 break;
 
             case EVENT_POLL_PDP:
@@ -1351,7 +2198,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
                  * PDP context and notify us with PDP_CONTEXT_CHANGED.
                  * But we should stop the network polling and prevent reset PDP.
                  */
-                log("[DSAC DEB] " + "EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
+                if (DBG) log("EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
                 stopNetStatPoll();
                 mIsPsRestricted = true;
                 break;
@@ -1361,20 +2208,39 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
                  * When PS restrict is removed, we need setup PDP connection if
                  * PDP connection is down.
                  */
-                log("[DSAC DEB] " + "EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted);
+                if (DBG) log("EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted);
                 mIsPsRestricted  = false;
-                if (mState == State.CONNECTED) {
+                if (isConnected()) {
                     startNetStatPoll();
                 } else {
+                    // TODO: Should all PDN states be checked to fail?
                     if (mState == State.FAILED) {
-                        cleanUpConnection(false, Phone.REASON_PS_RESTRICT_ENABLED);
-                        mRetryMgr.resetRetryCount();
+                        cleanUpAllConnections(false, Phone.REASON_PS_RESTRICT_ENABLED);
+                        resetAllRetryCounts();
                         mReregisterOnReconnectFailure = false;
                     }
-                    trySetupData(Phone.REASON_PS_RESTRICT_ENABLED);
+                    trySetupData(Phone.REASON_PS_RESTRICT_ENABLED, Phone.APN_TYPE_DEFAULT);
+                }
+                break;
+            case EVENT_TRY_SETUP_DATA:
+                if (msg.obj instanceof ApnContext) {
+                    onTrySetupData((ApnContext)msg.obj);
+                } else if (msg.obj instanceof String) {
+                    onTrySetupData((String)msg.obj);
+                } else {
+                    loge("EVENT_TRY_SETUP request w/o apnContext or String");
                 }
                 break;
 
+            case EVENT_CLEAN_UP_CONNECTION:
+                boolean tearDown = (msg.arg1 == 0) ? false : true;
+                if (DBG) log("EVENT_CLEAN_UP_CONNECTION tearDown=" + tearDown);
+                if (msg.obj instanceof ApnContext) {
+                    cleanUpConnection(tearDown, (ApnContext)msg.obj);
+                } else {
+                    loge("EVENT_CLEAN_UP_CONNECTION request w/o apn context");
+                }
+                break;
             default:
                 // handle the message in the super class DataConnectionTracker
                 super.handleMessage(msg);
@@ -1382,13 +2248,44 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
         }
     }
 
+    protected int getApnProfileID(String apnType) {
+        if (TextUtils.equals(apnType, Phone.APN_TYPE_IMS)) {
+            return RILConstants.DATA_PROFILE_IMS;
+        } else if (TextUtils.equals(apnType, Phone.APN_TYPE_FOTA)) {
+            return RILConstants.DATA_PROFILE_FOTA;
+        } else if (TextUtils.equals(apnType, Phone.APN_TYPE_CBS)) {
+            return RILConstants.DATA_PROFILE_CBS;
+        } else {
+            return RILConstants.DATA_PROFILE_DEFAULT;
+        }
+    }
+
+    private int getCellLocationId() {
+        int cid = -1;
+        CellLocation loc = mPhone.getCellLocation();
+
+        if (loc != null) {
+            if (loc instanceof GsmCellLocation) {
+                cid = ((GsmCellLocation)loc).getCid();
+            } else if (loc instanceof CdmaCellLocation) {
+                cid = ((CdmaCellLocation)loc).getBaseStationId();
+            }
+        }
+        return cid;
+    }
+
+    @Override
+    public boolean isAnyActiveDataConnections() {
+        return isConnected();
+    }
+
     @Override
     protected void log(String s) {
-        Log.d(LOG_TAG, "[GsmDataConnectionTracker] " + s);
+        Log.d(LOG_TAG, "[GsmDCT] "+ s);
     }
 
     @Override
     protected void loge(String s) {
-        Log.e(LOG_TAG, "[GsmDataConnectionTracker] " + s);
+        Log.e(LOG_TAG, "[GsmDCT] " + s);
     }
 }
index 2962e0f..680b3cd 100644 (file)
@@ -765,7 +765,7 @@ public final class GsmMmiCode extends Handler implements MmiCode {
                         // invalid length
                         handlePasswordError(com.android.internal.R.string.invalidPin);
                     } else if (sc.equals(SC_PIN) &&
-                               phone.mSimCard.getState() == SimCard.State.PUK_REQUIRED ) {
+                               phone.mIccCard.getState() == SimCard.State.PUK_REQUIRED ) {
                         // Sim is puk-locked
                         handlePasswordError(com.android.internal.R.string.needPuk);
                     } else {
@@ -885,7 +885,7 @@ public final class GsmMmiCode extends Handler implements MmiCode {
                 */
                 if ((ar.exception == null) && (msg.arg1 == 1)) {
                     boolean cffEnabled = (msg.arg2 == 1);
-                    phone.mSIMRecords.setVoiceCallForwardingFlag(1, cffEnabled);
+                    phone.mIccRecords.setVoiceCallForwardingFlag(1, cffEnabled);
                 }
 
                 onSetComplete(ar);
@@ -1203,7 +1203,7 @@ public final class GsmMmiCode extends Handler implements MmiCode {
                 (info.serviceClass & serviceClassMask)
                         == CommandsInterface.SERVICE_CLASS_VOICE) {
             boolean cffEnabled = (info.status == 1);
-            phone.mSIMRecords.setVoiceCallForwardingFlag(1, cffEnabled);
+            phone.mIccRecords.setVoiceCallForwardingFlag(1, cffEnabled);
         }
 
         return TextUtils.replace(template, sources, destinations);
@@ -1228,7 +1228,7 @@ public final class GsmMmiCode extends Handler implements MmiCode {
                 sb.append(context.getText(com.android.internal.R.string.serviceDisabled));
 
                 // Set unconditional CFF in SIM to false
-                phone.mSIMRecords.setVoiceCallForwardingFlag(1, false);
+                phone.mIccRecords.setVoiceCallForwardingFlag(1, false);
             } else {
 
                 SpannableStringBuilder tb = new SpannableStringBuilder();
index f576b4e..21a12f1 100755 (executable)
@@ -99,7 +99,7 @@ final class GsmSMSDispatcher extends SMSDispatcher {
 
     /** {@inheritDoc} */
     @Override
-    protected int dispatchMessage(SmsMessageBase smsb) {
+    public int dispatchMessage(SmsMessageBase smsb) {
 
         // If sms is null, means there was a parsing error.
         if (smsb == null) {
@@ -383,7 +383,7 @@ final class GsmSMSDispatcher extends SMSDispatcher {
 
     /** {@inheritDoc} */
     @Override
-    protected void activateCellBroadcastSms(int activate, Message response) {
+    public void activateCellBroadcastSms(int activate, Message response) {
         // Unless CBS is implemented for GSM, this point should be unreachable.
         Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM.");
         response.recycle();
@@ -391,7 +391,7 @@ final class GsmSMSDispatcher extends SMSDispatcher {
 
     /** {@inheritDoc} */
     @Override
-    protected void getCellBroadcastSmsConfig(Message response){
+    public void getCellBroadcastSmsConfig(Message response){
         // Unless CBS is implemented for GSM, this point should be unreachable.
         Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM.");
         response.recycle();
@@ -399,7 +399,7 @@ final class GsmSMSDispatcher extends SMSDispatcher {
 
     /** {@inheritDoc} */
     @Override
-    protected  void setCellBroadcastConfig(int[] configValuesArray, Message response) {
+    public  void setCellBroadcastConfig(int[] configValuesArray, Message response) {
         // Unless CBS is implemented for GSM, this point should be unreachable.
         Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM.");
         response.recycle();
index ac83808..a7631cd 100644 (file)
@@ -23,6 +23,7 @@ import com.android.internal.telephony.EventLogTags;
 import com.android.internal.telephony.IccCard;
 import com.android.internal.telephony.MccTable;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.RestrictedState;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.TelephonyIntents;
@@ -75,7 +76,6 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
     GsmCellLocation cellLoc;
     GsmCellLocation newCellLoc;
     int mPreferredNetworkType;
-    RestrictedState rs;
 
     private int gprsState = ServiceState.STATE_OUT_OF_SERVICE;
     private int newGPRSState = ServiceState.STATE_OUT_OF_SERVICE;
@@ -85,12 +85,6 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
     private int mNewReasonDataDenied = -1;
 
     /**
-     *  Values correspond to ServiceStateTracker.DATA_ACCESS_ definitions.
-     */
-    private int networkType = 0;
-    private int newNetworkType = 0;
-
-    /**
      * GSM roaming status solely based on TS 27.007 7.2 CREG. Only used by
      * handlePollStateResult to store CREG roaming result.
      */
@@ -107,11 +101,6 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
      */
     private boolean mEmergencyOnly = false;
 
-    private RegistrantList gprsAttachedRegistrants = new RegistrantList();
-    private RegistrantList gprsDetachedRegistrants = new RegistrantList();
-    private RegistrantList psRestrictEnabledRegistrants = new RegistrantList();
-    private RegistrantList psRestrictDisabledRegistrants = new RegistrantList();
-
     /**
      * Sometimes we get the NITZ time before we know what country we
      * are in. Keep the time zone information from the NITZ string so
@@ -169,8 +158,6 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
     static final int PS_NOTIFICATION = 888;  // Id to update and cancel PS restricted
     static final int CS_NOTIFICATION = 999;  // Id to update and cancel CS restricted
 
-    static final int MAX_NUM_DATA_STATE_READS = 15;
-
     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -206,7 +193,6 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
         newSS = new ServiceState();
         cellLoc = new GsmCellLocation();
         newCellLoc = new GsmCellLocation();
-        rs = new RestrictedState();
         mSignalStrength = new SignalStrength();
 
         PowerManager powerManager =
@@ -255,7 +241,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
         cm.unregisterForVoiceNetworkStateChanged(this);
         cm.unregisterForSIMReady(this);
 
-        phone.mSIMRecords.unregisterForRecordsLoaded(this);
+        phone.mIccRecords.unregisterForRecordsLoaded(this);
         cm.unSetOnSignalStrengthUpdate(this);
         cm.unSetOnRestrictedStateChanged(this);
         cm.unSetOnNITZTime(this);
@@ -264,7 +250,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
     }
 
     protected void finalize() {
-        if(DBG) Log.d(LOG_TAG, "GsmServiceStateTracker finalized");
+        if(DBG) log("finalize");
     }
 
     @Override
@@ -272,97 +258,6 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
         return phone;
     }
 
-    /**
-     * Registration point for transition into GPRS attached.
-     * @param h handler to notify
-     * @param what what code of message when delivered
-     * @param obj placed in Message.obj
-     */
-    void registerForGprsAttached(Handler h, int what, Object obj) {
-        Registrant r = new Registrant(h, what, obj);
-        gprsAttachedRegistrants.add(r);
-
-        if (gprsState == ServiceState.STATE_IN_SERVICE) {
-            r.notifyRegistrant();
-        }
-    }
-
-    void unregisterForGprsAttached(Handler h) {
-        gprsAttachedRegistrants.remove(h);
-    }
-
-    void registerForNetworkAttach(Handler h, int what, Object obj) {
-        Registrant r = new Registrant(h, what, obj);
-        networkAttachedRegistrants.add(r);
-
-        if (ss.getState() == ServiceState.STATE_IN_SERVICE) {
-            r.notifyRegistrant();
-        }
-    }
-
-    void unregisterForNetworkAttach(Handler h) {
-        networkAttachedRegistrants.remove(h);
-    }
-
-    /**
-     * Registration point for transition into GPRS detached.
-     * @param h handler to notify
-     * @param what what code of message when delivered
-     * @param obj placed in Message.obj
-     */
-    void registerForGprsDetached(Handler h, int what, Object obj) {
-        Registrant r = new Registrant(h, what, obj);
-        gprsDetachedRegistrants.add(r);
-
-        if (gprsState == ServiceState.STATE_OUT_OF_SERVICE) {
-            r.notifyRegistrant();
-        }
-    }
-
-    void unregisterForGprsDetached(Handler h) {
-        gprsDetachedRegistrants.remove(h);
-    }
-
-    /**
-     * Registration point for transition into packet service restricted zone.
-     * @param h handler to notify
-     * @param what what code of message when delivered
-     * @param obj placed in Message.obj
-     */
-    void registerForPsRestrictedEnabled(Handler h, int what, Object obj) {
-        Log.d(LOG_TAG, "[DSAC DEB] " + "registerForPsRestrictedEnabled ");
-        Registrant r = new Registrant(h, what, obj);
-        psRestrictEnabledRegistrants.add(r);
-
-        if (rs.isPsRestricted()) {
-            r.notifyRegistrant();
-        }
-    }
-
-    void unregisterForPsRestrictedEnabled(Handler h) {
-        psRestrictEnabledRegistrants.remove(h);
-    }
-
-    /**
-     * Registration point for transition out of packet service restricted zone.
-     * @param h handler to notify
-     * @param what what code of message when delivered
-     * @param obj placed in Message.obj
-     */
-    void registerForPsRestrictedDisabled(Handler h, int what, Object obj) {
-        Log.d(LOG_TAG, "[DSAC DEB] " + "registerForPsRestrictedDisabled ");
-        Registrant r = new Registrant(h, what, obj);
-        psRestrictDisabledRegistrants.add(r);
-
-        if (rs.isPsRestricted()) {
-            r.notifyRegistrant();
-        }
-    }
-
-    void unregisterForPsRestrictedDisabled(Handler h) {
-        psRestrictDisabledRegistrants.remove(h);
-    }
-
     public void handleMessage (Message msg) {
         AsyncResult ar;
         int[] ints;
@@ -380,7 +275,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
                 // it has been unlocked. At this stage, the radio is already
                 // powered on.
                 if (mNeedToRegForSimLoaded) {
-                    phone.mSIMRecords.registerForRecordsLoaded(this,
+                    phone.mIccRecords.registerForRecordsLoaded(this,
                             EVENT_SIM_RECORDS_LOADED, null);
                     mNeedToRegForSimLoaded = false;
                 }
@@ -548,7 +443,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
                 // This is a notification from
                 // CommandsInterface.setOnRestrictedStateChanged
 
-                Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_RESTRICTED_STATE_CHANGED");
+                if (DBG) log("EVENT_RESTRICTED_STATE_CHANGED");
 
                 ar = (AsyncResult) msg.obj;
 
@@ -556,7 +451,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
                 break;
 
             default:
-                Log.e(LOG_TAG, "Unhandled message with number: " + msg.what);
+                super.handleMessage(msg);
             break;
         }
     }
@@ -567,36 +462,14 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
             && cm.getRadioState() == CommandsInterface.RadioState.RADIO_OFF) {
             cm.setRadioPower(true, null);
         } else if (!mDesiredPowerState && cm.getRadioState().isOn()) {
-            DataConnectionTracker dcTracker = phone.mDataConnection;
-            if (! dcTracker.isDataConnectionAsDesired()) {
-                EventLog.writeEvent(EventLogTags.DATA_NETWORK_STATUS_ON_RADIO_OFF,
-                        dcTracker.getStateInString(), dcTracker.getAnyDataEnabled() ? 1 : 0);
-            }
             // If it's on and available and we want it off gracefully
-            powerOffRadioSafely();
+            DataConnectionTracker dcTracker = phone.mDataConnectionTracker;
+            powerOffRadioSafely(dcTracker);
         } // Otherwise, we're in the desired state
     }
 
     @Override
-    protected void powerOffRadioSafely() {
-        // clean data connection
-        DataConnectionTracker dcTracker = phone.mDataConnection;
-        Message msg = dcTracker.obtainMessage(DataConnectionTracker.EVENT_CLEAN_UP_CONNECTION);
-        msg.arg1 = 1; // tearDown is true
-        msg.obj = GSMPhone.REASON_RADIO_TURNED_OFF;
-        dcTracker.sendMessage(msg);
-
-        // poll data state up to 15 times, with a 100ms delay
-        // totaling 1.5 sec. Normal data disable action will finish in 100ms.
-        for (int i = 0; i < MAX_NUM_DATA_STATE_READS; i++) {
-            if (dcTracker.getState() != DataConnectionTracker.State.CONNECTED
-                    && dcTracker.getState() != DataConnectionTracker.State.DISCONNECTING) {
-                Log.d(LOG_TAG, "Data shutdown complete.");
-                break;
-            }
-            SystemClock.sleep(DATA_STATE_POLL_SLEEP_MS);
-        }
-
+    protected void hangupAndPowerOff() {
         // hang up all active voice calls
         if (phone.isInCall()) {
             phone.mCT.ringingCall.hangupIfAlive();
@@ -608,15 +481,15 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
     }
 
     protected void updateSpnDisplay() {
-        int rule = phone.mSIMRecords.getDisplayRule(ss.getOperatorNumeric());
-        String spn = phone.mSIMRecords.getServiceProviderName();
+        int rule = phone.mIccRecords.getDisplayRule(ss.getOperatorNumeric());
+        String spn = phone.mIccRecords.getServiceProviderName();
         String plmn = ss.getOperatorAlphaLong();
 
         // For emergency calls only, pass the EmergencyCallsOnly string via EXTRA_PLMN
         if (mEmergencyOnly && cm.getRadioState().isOn()) {
             plmn = Resources.getSystem().
                 getText(com.android.internal.R.string.emergency_calls_only).toString();
-            Log.d(LOG_TAG, "updateSpnDisplay: emergency only and radio is on plmn='" + plmn + "'");
+            if (DBG) log("updateSpnDisplay: emergency only and radio is on plmn='" + plmn + "'");
         }
 
         if (rule != curSpnRule
@@ -627,10 +500,11 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
             boolean showPlmn = !TextUtils.isEmpty(plmn) &&
                 (rule & SIMRecords.SPN_RULE_SHOW_PLMN) == SIMRecords.SPN_RULE_SHOW_PLMN;
 
-            Log.d(LOG_TAG,
-                    String.format("updateSpnDisplay: changed sending intent" + " rule=" + rule +
+            if (DBG) {
+                log(String.format("updateSpnDisplay: changed sending intent" + " rule=" + rule +
                             " showPlmn='%b' plmn='%s' showSpn='%b' spn='%s'",
                             showPlmn, plmn, showSpn, spn));
+            }
             Intent intent = new Intent(Intents.SPN_STRINGS_UPDATED_ACTION);
             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
             intent.putExtra(Intents.EXTRA_SHOW_SPN, showSpn);
@@ -674,10 +548,8 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
                 return;
             }
 
-            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" +
+            if (err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW) {
+                loge("RIL implementation has returned an error where it must succeed" +
                         ar.exception);
             }
         } else try {
@@ -706,7 +578,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
                                 }
                             }
                         } catch (NumberFormatException ex) {
-                            Log.w(LOG_TAG, "error parsing RegistrationState: " + ex);
+                            loge("error parsing RegistrationState: " + ex);
                         }
                     }
 
@@ -746,12 +618,12 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
                                 mNewMaxDataCalls = Integer.parseInt(states[5]);
                             }
                         } catch (NumberFormatException ex) {
-                            Log.w(LOG_TAG, "error parsing GprsRegistrationState: " + ex);
+                            loge("error parsing GprsRegistrationState: " + ex);
                         }
                     }
                     newGPRSState = regCodeToServiceState(regState);
                     mDataRoaming = regCodeIsRoaming(regState);
-                    newNetworkType = type;
+                    mNewRadioTechnology = type;
                     newSS.setRadioTechnology(type);
                 break;
 
@@ -771,8 +643,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
             }
 
         } catch (RuntimeException ex) {
-            Log.e(LOG_TAG, "Exception while polling service state. "
-                            + "Probably malformed RIL response.", ex);
+            loge("Exception while polling service state. Probably malformed RIL response." + ex);
         }
 
         pollingContext[0]--;
@@ -835,7 +706,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
             case RUIM_LOCKED_OR_ABSENT:
             case NV_NOT_READY:
             case NV_READY:
-                Log.d(LOG_TAG, "Radio Technology Change ongoing, setting SS to off");
+                if (DBG) log("Radio Technology Change ongoing, setting SS to off");
                 newSS.setStateOff();
                 newCellLoc.setStateInvalid();
                 setSignalStrengthDefaultValues();
@@ -872,48 +743,17 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
         }
     }
 
-    private static String networkTypeToString(int type) {
-        //Network Type from GPRS_REGISTRATION_STATE
-        String ret = "unknown";
-
-        switch (type) {
-            case DATA_ACCESS_GPRS:
-                ret = "GPRS";
-                break;
-            case DATA_ACCESS_EDGE:
-                ret = "EDGE";
-                break;
-            case DATA_ACCESS_UMTS:
-                ret = "UMTS";
-                break;
-            case DATA_ACCESS_HSDPA:
-                ret = "HSDPA";
-                break;
-            case DATA_ACCESS_HSUPA:
-                ret = "HSUPA";
-                break;
-            case DATA_ACCESS_HSPA:
-                ret = "HSPA";
-                break;
-            default:
-                Log.e(LOG_TAG, "Wrong network type: " + Integer.toString(type));
-                break;
-        }
-
-        return ret;
-    }
-
     private void pollStateDone() {
         if (DBG) {
-            Log.d(LOG_TAG, "Poll ServiceState done: " +
+            log("Poll ServiceState done: " +
                 " oldSS=[" + ss + "] newSS=[" + newSS +
                 "] oldGprs=" + gprsState + " newData=" + newGPRSState +
                 " oldMaxDataCalls=" + mMaxDataCalls +
                 " mNewMaxDataCalls=" + mNewMaxDataCalls +
                 " oldReasonDataDenied=" + mReasonDataDenied +
                 " mNewReasonDataDenied=" + mNewReasonDataDenied +
-                " oldType=" + networkTypeToString(networkType) +
-                " newType=" + networkTypeToString(newNetworkType));
+                " oldType=" + ServiceState.radioTechnologyToString(mRadioTechnology) +
+                " newType=" + ServiceState.radioTechnologyToString(mNewRadioTechnology));
         }
 
         boolean hasRegistered =
@@ -932,7 +772,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
                 gprsState == ServiceState.STATE_IN_SERVICE
                 && newGPRSState != ServiceState.STATE_IN_SERVICE;
 
-        boolean hasNetworkTypeChanged = networkType != newNetworkType;
+        boolean hasRadioTechnologyChanged = mRadioTechnology != mNewRadioTechnology;
 
         boolean hasChanged = !newSS.equals(ss);
 
@@ -962,33 +802,36 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
         // Add an event log when network type switched
         // TODO: we may add filtering to reduce the event logged,
         // i.e. check preferred network setting, only switch to 2G, etc
-        if (hasNetworkTypeChanged) {
+        if (hasRadioTechnologyChanged) {
             int cid = -1;
             GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation());
             if (loc != null) cid = loc.getCid();
-            EventLog.writeEvent(EventLogTags.GSM_RAT_SWITCHED, cid, networkType, newNetworkType);
-            Log.d(LOG_TAG,
-                    "RAT switched " + networkTypeToString(networkType) + " -> "
-                    + networkTypeToString(newNetworkType) + " at cell " + cid);
+            EventLog.writeEvent(EventLogTags.GSM_RAT_SWITCHED, cid, mRadioTechnology,
+                    mNewRadioTechnology);
+            if (DBG) {
+                log("RAT switched " + ServiceState.radioTechnologyToString(mRadioTechnology) +
+                        " -> " + ServiceState.radioTechnologyToString(mNewRadioTechnology) +
+                        " at cell " + cid);
+            }
         }
 
         gprsState = newGPRSState;
         mReasonDataDenied = mNewReasonDataDenied;
         mMaxDataCalls = mNewMaxDataCalls;
-        networkType = newNetworkType;
+        mRadioTechnology = mNewRadioTechnology;
         // this new state has been applied - forget it until we get a new new state
-        newNetworkType = 0;
+        mNewRadioTechnology = 0;
 
 
         newSS.setStateOutOfService(); // clean slate for next time
 
-        if (hasNetworkTypeChanged) {
+        if (hasRadioTechnologyChanged) {
             phone.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
-                    networkTypeToString(networkType));
+                    ServiceState.radioTechnologyToString(mRadioTechnology));
         }
 
         if (hasRegistered) {
-            networkAttachedRegistrants.notifyRegistrants();
+            mNetworkAttachedRegistrants.notifyRegistrants();
         }
 
         if (hasChanged) {
@@ -1010,9 +853,9 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
                     iso = MccTable.countryCodeForMcc(Integer.parseInt(
                             operatorNumeric.substring(0,3)));
                 } catch ( NumberFormatException ex){
-                    Log.w(LOG_TAG, "countryCodeForMcc error" + ex);
+                    loge("countryCodeForMcc error" + ex);
                 } catch ( StringIndexOutOfBoundsException ex) {
-                    Log.w(LOG_TAG, "countryCodeForMcc error" + ex);
+                    loge("countryCodeForMcc error" + ex);
                 }
 
                 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, iso);
@@ -1065,23 +908,23 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
         }
 
         if (hasGprsAttached) {
-            gprsAttachedRegistrants.notifyRegistrants();
+            mAttachedRegistrants.notifyRegistrants();
         }
 
         if (hasGprsDetached) {
-            gprsDetachedRegistrants.notifyRegistrants();
+            mDetachedRegistrants.notifyRegistrants();
         }
 
-        if (hasNetworkTypeChanged) {
-            phone.notifyDataConnection();
+        if (hasRadioTechnologyChanged) {
+            phone.notifyDataConnection(Phone.REASON_NW_TYPE_CHANGED, Phone.APN_TYPE_ALL);
         }
 
         if (hasRoamingOn) {
-            roamingOnRegistrants.notifyRegistrants();
+            mRoamingOnRegistrants.notifyRegistrants();
         }
 
         if (hasRoamingOff) {
-            roamingOffRegistrants.notifyRegistrants();
+            mRoamingOffRegistrants.notifyRegistrants();
         }
 
         if (hasLocationChanged) {
@@ -1125,10 +968,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
             // Couldn't find a proper timezone.  Perhaps the DST data is wrong.
             guess = findTimeZone(offset, !dst, when);
         }
-        if (DBG) {
-            Log.d(LOG_TAG, "getNitzTimeZone returning "
-                    + (guess == null ? guess : guess.getID()));
-        }
+        if (DBG) log("getNitzTimeZone returning " + (guess == null ? guess : guess.getID()));
         return guess;
     }
 
@@ -1199,7 +1039,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
                 lteRssnr = ints[10];
                 lteCqi = ints[11];
             } else {
-                Log.e(LOG_TAG, "Bogus signal strength response");
+                loge("Bogus signal strength response");
                 rssi = 99;
             }
         }
@@ -1226,10 +1066,9 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
      * @param ar an int value of RIL_RESTRICTED_STATE_*
      */
     private void onRestrictedStateChanged(AsyncResult ar) {
-        Log.d(LOG_TAG, "[DSAC DEB] " + "onRestrictedStateChanged");
         RestrictedState newRs = new RestrictedState();
 
-        Log.d(LOG_TAG, "[DSAC DEB] " + "current rs at enter "+ rs);
+        if (DBG) log("onRestrictedStateChanged: E rs "+ mRestrictedState);
 
         if (ar.exception == null) {
             int[] ints = (int[])ar.result;
@@ -1247,13 +1086,13 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
                         (state & RILConstants.RIL_RESTRICTED_STATE_PS_ALL)!= 0);
             }
 
-            Log.d(LOG_TAG, "[DSAC DEB] " + "new rs "+ newRs);
+            if (DBG) log("onRestrictedStateChanged: new rs "+ newRs);
 
-            if (!rs.isPsRestricted() && newRs.isPsRestricted()) {
-                psRestrictEnabledRegistrants.notifyRegistrants();
+            if (!mRestrictedState.isPsRestricted() && newRs.isPsRestricted()) {
+                mPsRestrictEnabledRegistrants.notifyRegistrants();
                 setNotification(PS_ENABLED);
-            } else if (rs.isPsRestricted() && !newRs.isPsRestricted()) {
-                psRestrictDisabledRegistrants.notifyRegistrants();
+            } else if (mRestrictedState.isPsRestricted() && !newRs.isPsRestricted()) {
+                mPsRestrictDisabledRegistrants.notifyRegistrants();
                 setNotification(PS_DISABLED);
             }
 
@@ -1262,7 +1101,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
              * there are 4 x 4 combinations in current and new restricted states
              * and we only need to notify when state is changed.
              */
-            if (rs.isCsRestricted()) {
+            if (mRestrictedState.isCsRestricted()) {
                 if (!newRs.isCsRestricted()) {
                     // remove all restriction
                     setNotification(CS_DISABLED);
@@ -1273,7 +1112,8 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
                     // remove emergency restriction
                     setNotification(CS_NORMAL_ENABLED);
                 }
-            } else if (rs.isCsEmergencyRestricted() && !rs.isCsNormalRestricted()) {
+            } else if (mRestrictedState.isCsEmergencyRestricted() &&
+                    !mRestrictedState.isCsNormalRestricted()) {
                 if (!newRs.isCsRestricted()) {
                     // remove all restriction
                     setNotification(CS_DISABLED);
@@ -1284,7 +1124,8 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
                     // remove emergency restriction and enable normal restriction
                     setNotification(CS_NORMAL_ENABLED);
                 }
-            } else if (!rs.isCsEmergencyRestricted() && rs.isCsNormalRestricted()) {
+            } else if (!mRestrictedState.isCsEmergencyRestricted() &&
+                    mRestrictedState.isCsNormalRestricted()) {
                 if (!newRs.isCsRestricted()) {
                     // remove all restriction
                     setNotification(CS_DISABLED);
@@ -1308,9 +1149,9 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
                 }
             }
 
-            rs = newRs;
+            mRestrictedState = newRs;
         }
-        Log.d(LOG_TAG, "[DSAC DEB] " + "current rs at return "+ rs);
+        log("onRestrictedStateChanged: X rs "+ mRestrictedState);
     }
 
     /** code is registration state 0-5 from TS 27.007 7.2 */
@@ -1334,7 +1175,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
                 return ServiceState.STATE_IN_SERVICE;
 
             default:
-                Log.w(LOG_TAG, "unexpected service state " + code);
+                loge("regCodeToServiceState: unexpected service state " + code);
                 return ServiceState.STATE_OUT_OF_SERVICE;
         }
     }
@@ -1401,12 +1242,16 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
         return gprsState;
     }
 
+    public int getCurrentDataConnectionState() {
+        return gprsState;
+    }
+
     /**
      * @return true if phone is camping on a technology (eg UMTS)
      * that could support voice and data simultaneously.
      */
-    boolean isConcurrentVoiceAndData() {
-        return (networkType >= DATA_ACCESS_UMTS);
+    public boolean isConcurrentVoiceAndDataAllowed() {
+        return (mRadioTechnology >= ServiceState.RADIO_TECHNOLOGY_UMTS);
     }
 
     /**
@@ -1450,8 +1295,9 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
         // tz is in number of quarter-hours
 
         long start = SystemClock.elapsedRealtime();
-        Log.i(LOG_TAG, "NITZ: " + nitz + "," + nitzReceiveTime +
+        if (DBG) {log("NITZ: " + nitz + "," + nitzReceiveTime +
                         " start=" + start + " delay=" + (start - nitzReceiveTime));
+        }
 
         try {
             /* NITZ time (hour:min:sec) will be in UTC but it supplies the timezone
@@ -1548,7 +1394,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
 
             String ignore = SystemProperties.get("gsm.ignore-nitz");
             if (ignore != null && ignore.equals("yes")) {
-                Log.i(LOG_TAG, "NITZ: Not setting clock because gsm.ignore-nitz is set");
+                log("NITZ: Not setting clock because gsm.ignore-nitz is set");
                 return;
             }
 
@@ -1561,43 +1407,49 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
 
                     if (millisSinceNitzReceived < 0) {
                         // Sanity check: something is wrong
-                        Log.i(LOG_TAG, "NITZ: not setting time, clock has rolled "
+                        if (DBG) {
+                            log("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 "
+                        if (DBG) {
+                            log("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);
+                    if (DBG) {
+                        log("NITZ: Setting time of day to " + c.getTime()
+                            + " NITZ receive delay(ms): " + millisSinceNitzReceived
+                            + " gained(ms): "
+                            + (c.getTimeInMillis() - System.currentTimeMillis())
+                            + " from " + nitz);
+                    }
 
                     setAndBroadcastNetworkSetTime(c.getTimeInMillis());
                     Log.i(LOG_TAG, "NITZ: after Setting time of day");
                 }
                 SystemProperties.set("gsm.nitz.time", String.valueOf(c.getTimeInMillis()));
                 saveNitzTime(c.getTimeInMillis());
-                if (Config.LOGV) {
+                if (DBG) {
                     long end = SystemClock.elapsedRealtime();
-                    Log.v(LOG_TAG, "NITZ: end=" + end + " dur=" + (end - start));
+                    log("NITZ: end=" + end + " dur=" + (end - start));
                 }
             } finally {
                 mWakeLock.release();
             }
         } catch (RuntimeException ex) {
-            Log.e(LOG_TAG, "NITZ: Parsing NITZ time " + nitz, ex);
+            loge("NITZ: Parsing NITZ time " + nitz + " ex=" + ex);
         }
     }
 
@@ -1663,8 +1515,10 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
                 Settings.System.AUTO_TIME, 0) == 0) {
             return;
         }
-        Log.d(LOG_TAG, "Reverting to NITZ Time: mSavedTime=" + mSavedTime
+        if (DBG) {
+            log("Reverting to NITZ Time: mSavedTime=" + mSavedTime
                 + " mSavedAtTime=" + mSavedAtTime);
+        }
         if (mSavedTime != 0 && mSavedAtTime != 0) {
             setAndBroadcastNetworkSetTime(mSavedTime
                     + (SystemClock.elapsedRealtime() - mSavedAtTime));
@@ -1676,7 +1530,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
                 Settings.System.AUTO_TIME_ZONE, 0) == 0) {
             return;
         }
-        Log.d(LOG_TAG, "Reverting to NITZ TimeZone: tz='" + mSavedTimeZone);
+        if (DBG) log("Reverting to NITZ TimeZone: tz='" + mSavedTimeZone);
         if (mSavedTimeZone != null) {
             setAndBroadcastNetworkSetTimeZone(mSavedTimeZone);
         }
@@ -1689,7 +1543,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
      */
     private void setNotification(int notifyType) {
 
-        Log.d(LOG_TAG, "[DSAC DEB] " + "create notification " + notifyType);
+        if (DBG) log("setNotification: create notification " + notifyType);
         Context context = phone.getContext();
 
         mNotification = new Notification();
@@ -1726,7 +1580,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
             break;
         }
 
-        Log.d(LOG_TAG, "[DSAC DEB] " + "put notification " + title + " / " +details);
+        if (DBG) log("setNotification: put notification " + title + " / " +details);
         mNotification.tickerText = title;
         mNotification.setLatestEventInfo(context, title, details,
                 mNotification.contentIntent);
@@ -1745,6 +1599,15 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
 
     @Override
     protected void log(String s) {
-        Log.d(LOG_TAG, "[GsmServiceStateTracker] " + s);
+        Log.d(LOG_TAG, "[GsmSST] " + s);
+    }
+
+    @Override
+    protected void loge(String s) {
+        Log.e(LOG_TAG, "[GsmSST] " + s);
+    }
+
+    private static void sloge(String s) {
+        Log.e(LOG_TAG, "[GsmSST] " + s);
     }
 }
index 3b133da..b0bad56 100755 (executable)
@@ -28,6 +28,7 @@ import android.util.Log;
 import com.android.internal.telephony.AdnRecord;
 import com.android.internal.telephony.AdnRecordCache;
 import com.android.internal.telephony.AdnRecordLoader;
+import com.android.internal.telephony.BaseCommands;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.IccFileHandler;
 import com.android.internal.telephony.IccRecords;
@@ -35,6 +36,8 @@ import com.android.internal.telephony.IccUtils;
 import com.android.internal.telephony.IccVmFixedException;
 import com.android.internal.telephony.IccVmNotSupportedException;
 import com.android.internal.telephony.MccTable;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneBase;
 
 import java.util.ArrayList;
 
@@ -42,12 +45,12 @@ import java.util.ArrayList;
 /**
  * {@hide}
  */
-public final class SIMRecords extends IccRecords {
-    static final String LOG_TAG = "GSM";
+public class SIMRecords extends IccRecords {
+    protected static final String LOG_TAG = "GSM";
 
     private static final boolean CRASH_RIL = false;
 
-    private static final boolean DBG = true;
+    protected static final boolean DBG = true;
 
     // ***** Instance Variables
 
@@ -58,8 +61,8 @@ public final class SIMRecords extends IccRecords {
 
     // ***** Cached SIM State; cleared on channel close
 
-    String imsi;
-    boolean callForwardingEnabled;
+    private String imsi;
+    private boolean callForwardingEnabled;
 
 
     /**
@@ -117,13 +120,13 @@ public final class SIMRecords extends IccRecords {
 
     private static final int EVENT_SIM_READY = 1;
     private static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 2;
-    private static final int EVENT_GET_IMSI_DONE = 3;
-    private static final int EVENT_GET_ICCID_DONE = 4;
+    protected static final int EVENT_GET_IMSI_DONE = 3;
+    protected static final int EVENT_GET_ICCID_DONE = 4;
     private static final int EVENT_GET_MBI_DONE = 5;
     private static final int EVENT_GET_MBDN_DONE = 6;
     private static final int EVENT_GET_MWIS_DONE = 7;
     private static final int EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE = 8;
-    private static final int EVENT_GET_AD_DONE = 9; // Admin data on SIM
+    protected static final int EVENT_GET_AD_DONE = 9; // Admin data on SIM
     private static final int EVENT_GET_MSISDN_DONE = 10;
     private static final int EVENT_GET_CPHS_MAILBOX_DONE = 11;
     private static final int EVENT_GET_SPN_DONE = 12;
@@ -144,6 +147,8 @@ public final class SIMRecords extends IccRecords {
     private static final int EVENT_GET_CFIS_DONE = 32;
     private static final int EVENT_GET_CSP_CPHS_DONE = 33;
 
+    protected static final int CSIM_EVENT_BASE = 100;
+
     // Lookup table for carriers known to produce SIMs which incorrectly indicate MNC length.
 
     private static final String[] MCCMNC_CODES_HAVING_3DIGITS_MNC = {
@@ -165,7 +170,7 @@ public final class SIMRecords extends IccRecords {
 
     // ***** Constructor
 
-    SIMRecords(GSMPhone p) {
+    public SIMRecords(PhoneBase p) {
         super(p);
 
         adnCache = new AdnRecordCache(phone);
@@ -178,23 +183,29 @@ public final class SIMRecords extends IccRecords {
         // recordsToLoad is set to 0 because no requests are made yet
         recordsToLoad = 0;
 
-
-        p.mCM.registerForSIMReady(this, EVENT_SIM_READY, null);
+        // SIMRecord is used by CDMA+LTE mode, and SIM_READY event
+        // will be subscribed by CdmaLteServiceStateTracker.
+        if (phone.getLteOnCdmaMode() != Phone.LTE_ON_CDMA_TRUE) {
+            p.mCM.registerForSIMReady(this, EVENT_SIM_READY, null);
+        }
         p.mCM.registerForOffOrNotAvailable(
                         this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
         p.mCM.setOnSmsOnSim(this, EVENT_SMS_ON_SIM, null);
-        p.mCM.setOnIccRefresh(this, EVENT_SIM_REFRESH, null);
+        p.mCM.registerForIccRefresh(this, EVENT_SIM_REFRESH, null);
 
         // Start off by setting empty state
         onRadioOffOrNotAvailable();
 
     }
 
+    @Override
     public void dispose() {
         //Unregister for all events
-        phone.mCM.unregisterForSIMReady(this);
+        if (phone.getLteOnCdmaMode() != Phone.LTE_ON_CDMA_TRUE) {
+            phone.mCM.unregisterForSIMReady(this);
+        }
         phone.mCM.unregisterForOffOrNotAvailable( this);
-        phone.mCM.unSetOnIccRefresh(this);
+        phone.mCM.unregisterForIccRefresh(this);
     }
 
     protected void finalize() {
@@ -230,7 +241,10 @@ public final class SIMRecords extends IccRecords {
 
     //***** Public Methods
 
-    /** Returns null if SIM is not yet ready */
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public String getIMSI() {
         return imsi;
     }
@@ -364,7 +378,7 @@ public final class SIMRecords extends IccRecords {
 
         countVoiceMessages = countWaiting;
 
-        ((GSMPhone) phone).notifyMessageWaitingIndicator();
+        phone.notifyMessageWaitingIndicator();
 
         try {
             if (efMWIS != null) {
@@ -403,17 +417,25 @@ public final class SIMRecords extends IccRecords {
         }
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public boolean getVoiceCallForwardingFlag() {
         return callForwardingEnabled;
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public void setVoiceCallForwardingFlag(int line, boolean enable) {
 
         if (line != 1) return; // only line 1 is supported
 
         callForwardingEnabled = enable;
 
-        ((GSMPhone) phone).notifyCallForwardingIndicator();
+        phone.notifyCallForwardingIndicator();
 
         try {
             if (mEfCfis != null) {
@@ -467,11 +489,17 @@ public final class SIMRecords extends IccRecords {
         }
     }
 
-    /** Returns the 5 or 6 digit MCC/MNC of the operator that
-     *  provided the SIM card. Returns null of SIM is not yet ready
+    /**
+     * {@inheritDoc}
      */
-    String getSIMOperatorNumeric() {
-        if (imsi == null || mncLength == UNINITIALIZED || mncLength == UNKNOWN) {
+    @Override
+    public String getOperatorNumeric() {
+        if (imsi == null) {
+            Log.d(LOG_TAG, "getOperatorNumeric: IMSI == null");
+            return null;
+        }
+        if (mncLength == UNINITIALIZED || mncLength == UNKNOWN) {
+            Log.d(LOG_TAG, "getSIMOperatorNumeric: bad mncLength");
             return null;
         }
 
@@ -518,7 +546,7 @@ public final class SIMRecords extends IccRecords {
                     imsi = null;
                 }
 
-                Log.d(LOG_TAG, "IMSI: " + imsi.substring(0, 6) + "xxxxxxx");
+                Log.d(LOG_TAG, "IMSI: " + /* imsi.substring(0, 6) +*/ "xxxxxxx");
 
                 if (((mncLength == UNKNOWN) || (mncLength == 2)) &&
                         ((imsi != null) && (imsi.length() >= 6))) {
@@ -547,7 +575,7 @@ public final class SIMRecords extends IccRecords {
                     // finally have both the imsi and the mncLength and can parse the imsi properly
                     MccTable.updateMccMncConfiguration(phone, imsi.substring(0, 3 + mncLength));
                 }
-                ((GSMPhone) phone).mSimCard.broadcastIccStateChangedIntent(
+                phone.mIccCard.broadcastIccStateChangedIntent(
                         SimCard.INTENT_VALUE_ICC_IMSI, null);
             break;
 
@@ -702,7 +730,7 @@ public final class SIMRecords extends IccRecords {
                     countVoiceMessages = -1;
                 }
 
-                ((GSMPhone) phone).notifyMessageWaitingIndicator();
+                phone.notifyMessageWaitingIndicator();
             break;
 
             case EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE:
@@ -731,7 +759,7 @@ public final class SIMRecords extends IccRecords {
                         countVoiceMessages = 0;
                     }
 
-                    ((GSMPhone) phone).notifyMessageWaitingIndicator();
+                    phone.notifyMessageWaitingIndicator();
                 }
             break;
 
@@ -842,7 +870,7 @@ public final class SIMRecords extends IccRecords {
                     callForwardingEnabled =
                         ((data[0] & CFF_LINE1_MASK) == CFF_UNCONDITIONAL_ACTIVE);
 
-                    ((GSMPhone) phone).notifyCallForwardingIndicator();
+                    phone.notifyCallForwardingIndicator();
                 }
                 break;
 
@@ -1042,7 +1070,7 @@ public final class SIMRecords extends IccRecords {
                 // Refer TS 51.011 Section 10.3.46 for the content description
                 callForwardingEnabled = ((data[1] & 0x01) != 0);
 
-                ((GSMPhone) phone).notifyCallForwardingIndicator();
+                phone.notifyCallForwardingIndicator();
                 break;
 
             case EVENT_GET_CSP_CPHS_DONE:
@@ -1152,7 +1180,7 @@ public final class SIMRecords extends IccRecords {
             System.arraycopy(ba, 1, pdu, 0, n - 1);
             SmsMessage message = SmsMessage.createFromPdu(pdu);
 
-            ((GSMPhone) phone).mSMS.dispatchMessage(message);
+            phone.mSMS.dispatchMessage(message);
         }
     }
 
@@ -1178,7 +1206,7 @@ public final class SIMRecords extends IccRecords {
                 System.arraycopy(ba, 1, pdu, 0, n - 1);
                 SmsMessage message = SmsMessage.createFromPdu(pdu);
 
-                ((GSMPhone) phone).mSMS.dispatchMessage(message);
+                phone.mSMS.dispatchMessage(message);
 
                 // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
                 // 1 == "received by MS from network; message read"
@@ -1209,7 +1237,7 @@ public final class SIMRecords extends IccRecords {
     protected void onAllRecordsLoaded() {
         Log.d(LOG_TAG, "SIMRecords: record load complete");
 
-        String operator = getSIMOperatorNumeric();
+        String operator = getOperatorNumeric();
 
         // Some fields require more than one SIM record to set
 
@@ -1228,7 +1256,7 @@ public final class SIMRecords extends IccRecords {
 
         recordsLoadedRegistrants.notifyRegistrants(
             new AsyncResult(null, null, null));
-        ((GSMPhone) phone).mSimCard.broadcastIccStateChangedIntent(
+        phone.mIccCard.broadcastIccStateChangedIntent(
                 SimCard.INTENT_VALUE_ICC_LOADED, null);
     }
 
@@ -1249,17 +1277,17 @@ public final class SIMRecords extends IccRecords {
         }
     }
 
-    private void onSimReady() {
+    public void onSimReady() {
         /* broadcast intent SIM_READY here so that we can make sure
           READY is sent before IMSI ready
         */
-        ((GSMPhone) phone).mSimCard.broadcastIccStateChangedIntent(
+        phone.mIccCard.broadcastIccStateChangedIntent(
                 SimCard.INTENT_VALUE_ICC_READY, null);
 
         fetchSimRecords();
     }
 
-    private void fetchSimRecords() {
+    protected void fetchSimRecords() {
         recordsRequested = true;
         IccFileHandler iccFh = phone.getIccFileHandler();
 
@@ -1350,7 +1378,8 @@ public final class SIMRecords extends IccRecords {
      *
      * If the SPN is not found on the SIM, the rule is always PLMN_ONLY.
      */
-    protected int getDisplayRule(String plmn) {
+    @Override
+    public int getDisplayRule(String plmn) {
         int rule;
         if (spn == null || spnDisplayCondition == -1) {
             // EF_SPN was not found on the SIM, or not yet loaded.  Just show ONS.
@@ -1377,7 +1406,7 @@ public final class SIMRecords extends IccRecords {
     private boolean isOnMatchingPlmn(String plmn) {
         if (plmn == null) return false;
 
-        if (plmn.equals(getSIMOperatorNumeric())) {
+        if (plmn.equals(getOperatorNumeric())) {
             return true;
         }
 
index 835cb29..643f709 100644 (file)
@@ -19,6 +19,10 @@ package com.android.internal.telephony.gsm;
 import android.util.Log;
 
 import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneBase;
+import com.android.internal.telephony.TelephonyProperties;
+import android.os.SystemProperties;
 
 /**
  * {@hide}
@@ -34,17 +38,39 @@ public final class SimCard extends IccCard {
         updateStateProperty();
     }
 
+    /**
+    * We have the Sim card for LTE on CDMA phone
+    */
+    public SimCard(PhoneBase phone, String logTag, Boolean dbg) {
+        super(phone, logTag, dbg);
+        mPhone.mCM.registerForSIMLockedOrAbsent(mHandler, EVENT_ICC_LOCKED_OR_ABSENT, null);
+        mPhone.mCM.registerForOffOrNotAvailable(mHandler, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
+        mPhone.mCM.registerForSIMReady(mHandler, EVENT_ICC_READY, null);
+        updateStateProperty();
+
+        if(mPhone.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE) {
+            mPhone.mCM.registerForNVReady(mHandler, EVENT_ICC_READY, null);
+            mPhone.mCM.registerForIccStatusChanged(mHandler, EVENT_ICC_LOCKED_OR_ABSENT, null);
+        }
+    }
+
     @Override
     public void dispose() {
+        super.dispose();
         //Unregister for all events
         mPhone.mCM.unregisterForSIMLockedOrAbsent(mHandler);
         mPhone.mCM.unregisterForOffOrNotAvailable(mHandler);
         mPhone.mCM.unregisterForSIMReady(mHandler);
+
+        if(mPhone.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE) {
+            mPhone.mCM.unregisterForNVReady(mHandler);
+            mPhone.mCM.unregisterForIccStatusChanged(mHandler);
+        }
     }
 
     @Override
     public String getServiceProviderName () {
-        return ((GSMPhone)mPhone).mSIMRecords.getServiceProviderName();
+        return mPhone.mIccRecords.getServiceProviderName();
     }
 
 }
index feb508a..377f8f0 100644 (file)
@@ -32,7 +32,7 @@ public class SimPhoneBookInterfaceManager extends IccPhoneBookInterfaceManager {
 
     public SimPhoneBookInterfaceManager(GSMPhone phone) {
         super(phone);
-        adnCache = phone.mSIMRecords.getAdnCache();
+        adnCache = phone.mIccRecords.getAdnCache();
         //NOTE service "simphonebook" added by IccSmsInterfaceManagerProxy
     }
 
index f2ece7f..5208ccd 100644 (file)
@@ -326,6 +326,10 @@ class SipCommandInterface extends BaseCommands implements CommandsInterface {
     public void reportStkServiceIsRunning(Message result) {
     }
 
+    @Override
+    public void getCdmaSubscriptionSource(Message response) {
+    }
+
     public void getGsmBroadcastConfig(Message response) {
     }
 
index d9bd7e8..80de9f3 100644 (file)
@@ -1001,6 +1001,11 @@ public final class SimulatedCommands extends BaseCommands
         resultSuccess(result, null);
     }
 
+    @Override
+    public void getCdmaSubscriptionSource(Message result) {
+        unimplemented(result);
+    }
+
     private boolean isSimLocked() {
         if (mSimLockedState != SimLockState.NONE) {
             return true;
index 3cde949..9ae26da 100644 (file)
@@ -16,8 +16,8 @@
 
 package android.net.wifi;
 
-import com.android.internal.util.HierarchicalState;
-import com.android.internal.util.HierarchicalStateMachine;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
 
 import android.net.wifi.WifiStateMachine.StateChangeResult;
 import android.content.Context;
@@ -33,7 +33,7 @@ import android.util.Log;
  * - detect a failed WPA handshake that loops indefinitely
  * - authentication failure handling
  */
-class SupplicantStateTracker extends HierarchicalStateMachine {
+class SupplicantStateTracker extends StateMachine {
 
     private static final String TAG = "SupplicantStateTracker";
     private static final boolean DBG = false;
@@ -53,14 +53,14 @@ class SupplicantStateTracker extends HierarchicalStateMachine {
 
     private Context mContext;
 
-    private HierarchicalState mUninitializedState = new UninitializedState();
-    private HierarchicalState mDefaultState = new DefaultState();
-    private HierarchicalState mInactiveState = new InactiveState();
-    private HierarchicalState mDisconnectState = new DisconnectedState();
-    private HierarchicalState mScanState = new ScanState();
-    private HierarchicalState mHandshakeState = new HandshakeState();
-    private HierarchicalState mCompletedState = new CompletedState();
-    private HierarchicalState mDormantState = new DormantState();
+    private State mUninitializedState = new UninitializedState();
+    private State mDefaultState = new DefaultState();
+    private State mInactiveState = new InactiveState();
+    private State mDisconnectState = new DisconnectedState();
+    private State mScanState = new ScanState();
+    private State mHandshakeState = new HandshakeState();
+    private State mCompletedState = new CompletedState();
+    private State mDormantState = new DormantState();
 
     public SupplicantStateTracker(Context context, WifiStateMachine wsm, Handler target) {
         super(TAG, target.getLooper());
@@ -146,7 +146,7 @@ class SupplicantStateTracker extends HierarchicalStateMachine {
      * HSM states
      *******************************************************/
 
-    class DefaultState extends HierarchicalState {
+    class DefaultState extends State {
         @Override
          public void enter() {
              if (DBG) Log.d(TAG, getName() + "\n");
@@ -188,21 +188,21 @@ class SupplicantStateTracker extends HierarchicalStateMachine {
      * or after we have lost the control channel
      * connection to the supplicant
      */
-    class UninitializedState extends HierarchicalState {
+    class UninitializedState extends State {
         @Override
          public void enter() {
              if (DBG) Log.d(TAG, getName() + "\n");
          }
     }
 
-    class InactiveState extends HierarchicalState {
+    class InactiveState extends State {
         @Override
          public void enter() {
              if (DBG) Log.d(TAG, getName() + "\n");
          }
     }
 
-    class DisconnectedState extends HierarchicalState {
+    class DisconnectedState extends State {
         @Override
          public void enter() {
              if (DBG) Log.d(TAG, getName() + "\n");
@@ -221,14 +221,14 @@ class SupplicantStateTracker extends HierarchicalStateMachine {
          }
     }
 
-    class ScanState extends HierarchicalState {
+    class ScanState extends State {
         @Override
          public void enter() {
              if (DBG) Log.d(TAG, getName() + "\n");
          }
     }
 
-    class HandshakeState extends HierarchicalState {
+    class HandshakeState extends State {
         /**
          * The max number of the WPA supplicant loop iterations before we
          * decide that the loop should be terminated:
@@ -277,7 +277,7 @@ class SupplicantStateTracker extends HierarchicalStateMachine {
         }
     }
 
-    class CompletedState extends HierarchicalState {
+    class CompletedState extends State {
         @Override
          public void enter() {
              if (DBG) Log.d(TAG, getName() + "\n");
@@ -318,7 +318,7 @@ class SupplicantStateTracker extends HierarchicalStateMachine {
     }
 
     //TODO: remove after getting rid of the state in supplicant
-    class DormantState extends HierarchicalState {
+    class DormantState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
index 6455d84..d83b968 100644 (file)
@@ -23,6 +23,7 @@ import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkUtils;
 import android.net.ProxyProperties;
+import android.net.RouteInfo;
 import android.net.wifi.WifiConfiguration.IpAssignment;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.wifi.WifiConfiguration.ProxySettings;
@@ -120,7 +121,7 @@ class WifiConfigStore {
     private static final String ipConfigFile = Environment.getDataDirectory() +
             "/misc/wifi/ipconfig.txt";
 
-    private static final int IPCONFIG_FILE_VERSION = 1;
+    private static final int IPCONFIG_FILE_VERSION = 2;
 
     /* IP and proxy configuration keys */
     private static final String ID_KEY = "id";
@@ -445,9 +446,8 @@ class WifiConfigStore {
             if (iter.hasNext()) {
                 LinkAddress linkAddress = iter.next();
                 dhcpInfoInternal.ipAddress = linkAddress.getAddress().getHostAddress();
-                Iterator<InetAddress>gateways = linkProperties.getGateways().iterator();
-                if (gateways.hasNext()) {
-                    dhcpInfoInternal.gateway = gateways.next().getHostAddress();
+                for (RouteInfo route : linkProperties.getRoutes()) {
+                    dhcpInfoInternal.addRoute(route);
                 }
                 dhcpInfoInternal.prefixLength = linkAddress.getNetworkPrefixLength();
                 Iterator<InetAddress> dnsIterator = linkProperties.getDnses().iterator();
@@ -478,6 +478,21 @@ class WifiConfigStore {
         }
     }
 
+    /**
+     * clear IP configuration for a given network id
+     */
+    static void clearIpConfiguration(int netId) {
+        synchronized (sConfiguredNetworks) {
+            WifiConfiguration config = sConfiguredNetworks.get(netId);
+            if (config != null && config.linkProperties != null) {
+                // Clear everything except proxy
+                ProxyProperties proxy = config.linkProperties.getHttpProxy();
+                config.linkProperties.clear();
+                config.linkProperties.setHttpProxy(proxy);
+            }
+        }
+    }
+
 
     /**
      * Fetch the proxy properties for a given network id
@@ -604,9 +619,22 @@ class WifiConfigStore {
                                     out.writeUTF(linkAddr.getAddress().getHostAddress());
                                     out.writeInt(linkAddr.getNetworkPrefixLength());
                                 }
-                                for (InetAddress gateway : linkProperties.getGateways()) {
+                                for (RouteInfo route : linkProperties.getRoutes()) {
                                     out.writeUTF(GATEWAY_KEY);
-                                    out.writeUTF(gateway.getHostAddress());
+                                    LinkAddress dest = route.getDestination();
+                                    if (dest != null) {
+                                        out.writeInt(1);
+                                        out.writeUTF(dest.getAddress().getHostAddress());
+                                        out.writeInt(dest.getNetworkPrefixLength());
+                                    } else {
+                                        out.writeInt(0);
+                                    }
+                                    if (route.getGateway() != null) {
+                                        out.writeInt(1);
+                                        out.writeUTF(route.getGateway().getHostAddress());
+                                    } else {
+                                        out.writeInt(0);
+                                    }
                                 }
                                 for (InetAddress inetAddr : linkProperties.getDnses()) {
                                     out.writeUTF(DNS_KEY);
@@ -682,7 +710,8 @@ class WifiConfigStore {
             in = new DataInputStream(new BufferedInputStream(new FileInputStream(
                     ipConfigFile)));
 
-            if (in.readInt() != IPCONFIG_FILE_VERSION) {
+            int version = in.readInt();
+            if (version != 2 && version != 1) {
                 Log.e(TAG, "Bad version on IP configuration file, ignore read");
                 return;
             }
@@ -709,8 +738,22 @@ class WifiConfigStore {
                                     NetworkUtils.numericToInetAddress(in.readUTF()), in.readInt());
                             linkProperties.addLinkAddress(linkAddr);
                         } else if (key.equals(GATEWAY_KEY)) {
-                            linkProperties.addGateway(
-                                    NetworkUtils.numericToInetAddress(in.readUTF()));
+                            LinkAddress dest = null;
+                            InetAddress gateway = null;
+                            if (version == 1) {
+                                // only supported default gateways - leave the dest/prefix empty
+                                gateway = NetworkUtils.numericToInetAddress(in.readUTF());
+                            } else {
+                                if (in.readInt() == 1) {
+                                    dest = new LinkAddress(
+                                            NetworkUtils.numericToInetAddress(in.readUTF()),
+                                            in.readInt());
+                                }
+                                if (in.readInt() == 1) {
+                                    gateway = NetworkUtils.numericToInetAddress(in.readUTF());
+                                }
+                            }
+                            linkProperties.addRoute(new RouteInfo(dest, gateway));
                         } else if (key.equals(DNS_KEY)) {
                             linkProperties.addDns(
                                     NetworkUtils.numericToInetAddress(in.readUTF()));
@@ -1022,22 +1065,21 @@ class WifiConfigStore {
                         .getLinkAddresses();
                 Collection<InetAddress> currentDnses = currentConfig.linkProperties.getDnses();
                 Collection<InetAddress> newDnses = newConfig.linkProperties.getDnses();
-                Collection<InetAddress> currentGateways =
-                        currentConfig.linkProperties.getGateways();
-                Collection<InetAddress> newGateways = newConfig.linkProperties.getGateways();
+                Collection<RouteInfo> currentRoutes = currentConfig.linkProperties.getRoutes();
+                Collection<RouteInfo> newRoutes = newConfig.linkProperties.getRoutes();
 
                 boolean linkAddressesDiffer =
                         (currentLinkAddresses.size() != newLinkAddresses.size()) ||
                         !currentLinkAddresses.containsAll(newLinkAddresses);
                 boolean dnsesDiffer = (currentDnses.size() != newDnses.size()) ||
                         !currentDnses.containsAll(newDnses);
-                boolean gatewaysDiffer = (currentGateways.size() != newGateways.size()) ||
-                        !currentGateways.containsAll(newGateways);
+                boolean routesDiffer = (currentRoutes.size() != newRoutes.size()) ||
+                        !currentRoutes.containsAll(newRoutes);
 
                 if ((currentConfig.ipAssignment != newConfig.ipAssignment) ||
                         linkAddressesDiffer ||
                         dnsesDiffer ||
-                        gatewaysDiffer) {
+                        routesDiffer) {
                     ipChanged = true;
                 }
                 break;
@@ -1112,8 +1154,8 @@ class WifiConfigStore {
         for (LinkAddress linkAddr : config.linkProperties.getLinkAddresses()) {
             linkProperties.addLinkAddress(linkAddr);
         }
-        for (InetAddress gateway : config.linkProperties.getGateways()) {
-            linkProperties.addGateway(gateway);
+        for (RouteInfo route : config.linkProperties.getRoutes()) {
+            linkProperties.addRoute(route);
         }
         for (InetAddress dns : config.linkProperties.getDnses()) {
             linkProperties.addDns(dns);
index 0c0e253..0769ea0 100644 (file)
@@ -74,9 +74,9 @@ import android.util.LruCache;
 
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.util.AsyncChannel;
-import com.android.internal.util.HierarchicalState;
-import com.android.internal.util.HierarchicalStateMachine;
 import com.android.internal.util.Protocol;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
 
 import java.net.InetAddress;
 import java.util.ArrayList;
@@ -90,7 +90,7 @@ import java.util.regex.Pattern;
  *
  * @hide
  */
-public class WifiStateMachine extends HierarchicalStateMachine {
+public class WifiStateMachine extends StateMachine {
 
     private static final String TAG = "WifiStateMachine";
     private static final String NETWORKTYPE = "WIFI";
@@ -364,50 +364,50 @@ public class WifiStateMachine extends HierarchicalStateMachine {
     private static final int SOFT_AP_RUNNING = 1;
 
     /* Default parent state */
-    private HierarchicalState mDefaultState = new DefaultState();
+    private State mDefaultState = new DefaultState();
     /* Temporary initial state */
-    private HierarchicalState mInitialState = new InitialState();
+    private State mInitialState = new InitialState();
     /* Unloading the driver */
-    private HierarchicalState mDriverUnloadingState = new DriverUnloadingState();
+    private State mDriverUnloadingState = new DriverUnloadingState();
     /* Loading the driver */
-    private HierarchicalState mDriverUnloadedState = new DriverUnloadedState();
+    private State mDriverUnloadedState = new DriverUnloadedState();
     /* Driver load/unload failed */
-    private HierarchicalState mDriverFailedState = new DriverFailedState();
+    private State mDriverFailedState = new DriverFailedState();
     /* Driver loading */
-    private HierarchicalState mDriverLoadingState = new DriverLoadingState();
+    private State mDriverLoadingState = new DriverLoadingState();
     /* Driver loaded */
-    private HierarchicalState mDriverLoadedState = new DriverLoadedState();
+    private State mDriverLoadedState = new DriverLoadedState();
     /* Driver loaded, waiting for supplicant to start */
-    private HierarchicalState mSupplicantStartingState = new SupplicantStartingState();
+    private State mSupplicantStartingState = new SupplicantStartingState();
     /* Driver loaded and supplicant ready */
-    private HierarchicalState mSupplicantStartedState = new SupplicantStartedState();
+    private State mSupplicantStartedState = new SupplicantStartedState();
     /* Waiting for supplicant to stop and monitor to exit */
-    private HierarchicalState mSupplicantStoppingState = new SupplicantStoppingState();
+    private State mSupplicantStoppingState = new SupplicantStoppingState();
     /* Driver start issued, waiting for completed event */
-    private HierarchicalState mDriverStartingState = new DriverStartingState();
+    private State mDriverStartingState = new DriverStartingState();
     /* Driver started */
-    private HierarchicalState mDriverStartedState = new DriverStartedState();
+    private State mDriverStartedState = new DriverStartedState();
     /* Driver stopping */
-    private HierarchicalState mDriverStoppingState = new DriverStoppingState();
+    private State mDriverStoppingState = new DriverStoppingState();
     /* Driver stopped */
-    private HierarchicalState mDriverStoppedState = new DriverStoppedState();
+    private State mDriverStoppedState = new DriverStoppedState();
     /* Scan for networks, no connection will be established */
-    private HierarchicalState mScanModeState = new ScanModeState();
+    private State mScanModeState = new ScanModeState();
     /* Connecting to an access point */
-    private HierarchicalState mConnectModeState = new ConnectModeState();
+    private State mConnectModeState = new ConnectModeState();
     /* Fetching IP after network connection (assoc+auth complete) */
-    private HierarchicalState mConnectingState = new ConnectingState();
+    private State mConnectingState = new ConnectingState();
     /* Connected with IP addr */
-    private HierarchicalState mConnectedState = new ConnectedState();
+    private State mConnectedState = new ConnectedState();
     /* disconnect issued, waiting for network disconnect confirmation */
-    private HierarchicalState mDisconnectingState = new DisconnectingState();
+    private State mDisconnectingState = new DisconnectingState();
     /* Network is not connected, supplicant assoc+auth is not complete */
-    private HierarchicalState mDisconnectedState = new DisconnectedState();
+    private State mDisconnectedState = new DisconnectedState();
     /* Waiting for WPS to be completed*/
-    private HierarchicalState mWaitForWpsCompletionState = new WaitForWpsCompletionState();
+    private State mWaitForWpsCompletionState = new WaitForWpsCompletionState();
 
     /* Soft Ap is running */
-    private HierarchicalState mSoftApStartedState = new SoftApStartedState();
+    private State mSoftApStartedState = new SoftApStartedState();
 
 
     /**
@@ -481,7 +481,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
         mNetworkInfo.setIsAvailable(false);
         mLinkProperties.clear();
         mLastBssid = null;
-        mLastNetworkId = -1;
+        mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
         mLastSignalLevel = -1;
 
         mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
@@ -821,11 +821,12 @@ public class WifiStateMachine extends HierarchicalStateMachine {
     }
 
     public void connectNetwork(WifiConfiguration wifiConfig) {
-        /* arg1 is used to indicate netId, force a netId value of -1 when
-         * we are passing a configuration since the default value of
-         * 0 is a valid netId
+        /* arg1 is used to indicate netId, force a netId value of
+         * WifiConfiguration.INVALID_NETWORK_ID when we are passing
+         * a configuration since the default value of 0 is a valid netId
          */
-        sendMessage(obtainMessage(CMD_CONNECT_NETWORK, -1, 0, wifiConfig));
+        sendMessage(obtainMessage(CMD_CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,
+                0, wifiConfig));
     }
 
     public void saveNetwork(WifiConfiguration wifiConfig) {
@@ -1361,7 +1362,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
         intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
-        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, mLinkProperties);
+        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties (mLinkProperties));
         if (bssid != null)
             intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
         mContext.sendStickyBroadcast(intent);
@@ -1377,7 +1378,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
     private void sendLinkConfigurationChangedBroadcast() {
         Intent intent = new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, mLinkProperties);
+        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties(mLinkProperties));
         mContext.sendBroadcast(intent);
     }
 
@@ -1412,9 +1413,9 @@ public class WifiStateMachine extends HierarchicalStateMachine {
         Log.d(TAG, "Reset connections and stopping DHCP");
 
         /*
-         * Reset connections & stop DHCP
+         * stop DHCP
          */
-        NetworkUtils.resetConnections(mInterfaceName);
+       NetworkUtils.resetConnections(mInterfaceName, NetworkUtils.RESET_ALL_ADDRESSES);
 
         if (mDhcpStateMachine != null) {
             mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
@@ -1429,7 +1430,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
         mWifiInfo.setInetAddress(null);
         mWifiInfo.setBSSID(null);
         mWifiInfo.setSSID(null);
-        mWifiInfo.setNetworkId(-1);
+        mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
         mWifiInfo.setRssi(MIN_RSSI);
         mWifiInfo.setLinkSpeed(-1);
 
@@ -1439,10 +1440,13 @@ public class WifiStateMachine extends HierarchicalStateMachine {
 
         /* Clear network properties */
         mLinkProperties.clear();
+        /* Clear IP settings if the network used DHCP */
+        if (!WifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
+            WifiConfigStore.clearIpConfiguration(mLastNetworkId);
+        }
 
         mLastBssid= null;
-        mLastNetworkId = -1;
-
+        mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
     }
 
     void handlePreDhcpSetup() {
@@ -1505,7 +1509,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
             if (!linkProperties.equals(mLinkProperties)) {
                 Log.d(TAG, "Link configuration changed for netId: " + mLastNetworkId
                     + " old: " + mLinkProperties + "new: " + linkProperties);
-                NetworkUtils.resetConnections(mInterfaceName);
+                NetworkUtils.resetConnections(mInterfaceName, NetworkUtils.RESET_ALL_ADDRESSES);
                 mLinkProperties = linkProperties;
                 sendLinkConfigurationChangedBroadcast();
             }
@@ -1674,7 +1678,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
      * HSM states
      *******************************************************/
 
-    class DefaultState extends HierarchicalState {
+    class DefaultState extends State {
         @Override
         public boolean processMessage(Message message) {
             if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
@@ -1757,7 +1761,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class InitialState extends HierarchicalState {
+    class InitialState extends State {
         @Override
         //TODO: could move logging into a common class
         public void enter() {
@@ -1778,7 +1782,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class DriverLoadingState extends HierarchicalState {
+    class DriverLoadingState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -1857,7 +1861,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class DriverLoadedState extends HierarchicalState {
+    class DriverLoadedState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -1898,7 +1902,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class DriverUnloadingState extends HierarchicalState {
+    class DriverUnloadingState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -1979,7 +1983,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class DriverUnloadedState extends HierarchicalState {
+    class DriverUnloadedState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -2000,7 +2004,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class DriverFailedState extends HierarchicalState {
+    class DriverFailedState extends State {
         @Override
         public void enter() {
             Log.e(TAG, getName() + "\n");
@@ -2014,7 +2018,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
     }
 
 
-    class SupplicantStartingState extends HierarchicalState {
+    class SupplicantStartingState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -2034,7 +2038,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
                     mWpsStateMachine.sendMessage(CMD_RESET_WPS_STATE);
                     /* Initialize data structures */
                     mLastBssid = null;
-                    mLastNetworkId = -1;
+                    mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
                     mLastSignalLevel = -1;
 
                     mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand());
@@ -2086,7 +2090,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class SupplicantStartedState extends HierarchicalState {
+    class SupplicantStartedState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -2209,7 +2213,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class SupplicantStoppingState extends HierarchicalState {
+    class SupplicantStoppingState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -2252,7 +2256,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class DriverStartingState extends HierarchicalState {
+    class DriverStartingState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -2293,7 +2297,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class DriverStartedState extends HierarchicalState {
+    class DriverStartedState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -2397,7 +2401,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class DriverStoppingState extends HierarchicalState {
+    class DriverStoppingState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -2433,7 +2437,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class DriverStoppedState extends HierarchicalState {
+    class DriverStoppedState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -2457,7 +2461,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class ScanModeState extends HierarchicalState {
+    class ScanModeState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -2494,7 +2498,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class ConnectModeState extends HierarchicalState {
+    class ConnectModeState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -2524,7 +2528,10 @@ public class WifiStateMachine extends HierarchicalStateMachine {
                     // Network id is only valid when we start connecting
                     if (SupplicantState.isConnecting(state)) {
                         mWifiInfo.setNetworkId(stateChangeResult.networkId);
+                    } else {
+                        mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
                     }
+
                     if (state == SupplicantState.ASSOCIATING) {
                         /* BSSID is valid only in ASSOCIATING state */
                         mWifiInfo.setBSSID(stateChangeResult.BSSID);
@@ -2604,7 +2611,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class ConnectingState extends HierarchicalState {
+    class ConnectingState extends State {
 
         @Override
         public void enter() {
@@ -2708,7 +2715,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
       }
     }
 
-    class ConnectedState extends HierarchicalState {
+    class ConnectedState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -2780,7 +2787,6 @@ public class WifiStateMachine extends HierarchicalStateMachine {
                     if (mWifiInfo.getNetworkId() == result.getNetworkId()) {
                         if (result.hasIpChanged()) {
                             Log.d(TAG,"Reconfiguring IP on connection");
-                            NetworkUtils.resetConnections(mInterfaceName);
                             transitionTo(mConnectingState);
                         }
                         if (result.hasProxyChanged()) {
@@ -2833,7 +2839,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class DisconnectingState extends HierarchicalState {
+    class DisconnectingState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -2863,7 +2869,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class DisconnectedState extends HierarchicalState {
+    class DisconnectedState extends State {
         private boolean mAlarmEnabled = false;
         private long mScanIntervalMs;
 
@@ -2970,7 +2976,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class WaitForWpsCompletionState extends HierarchicalState {
+    class WaitForWpsCompletionState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -3009,7 +3015,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class SoftApStartedState extends HierarchicalState {
+    class SoftApStartedState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
index 07900ae..338cb4d 100644 (file)
@@ -50,6 +50,7 @@ public class WifiStateTracker implements NetworkStateTracker {
     private LinkProperties mLinkProperties;
     private LinkCapabilities mLinkCapabilities;
     private NetworkInfo mNetworkInfo;
+    private NetworkInfo.State mLastState = NetworkInfo.State.UNKNOWN;
 
     /* For sending events to connectivity service handler */
     private Handler mCsHandler;
@@ -217,6 +218,14 @@ public class WifiStateTracker implements NetworkStateTracker {
                 if (mLinkCapabilities == null) {
                     mLinkCapabilities = new LinkCapabilities();
                 }
+                // don't want to send redundent state messages
+                // TODO can this be fixed in WifiStateMachine?
+                NetworkInfo.State state = mNetworkInfo.getState();
+                if (mLastState == state) {
+                    return;
+                } else {
+                    mLastState = state;
+                }
                 Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
                 msg.sendToTarget();
             } else if (intent.getAction().equals(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION)) {
@@ -228,4 +237,7 @@ public class WifiStateTracker implements NetworkStateTracker {
         }
     }
 
+    public void setDependencyMet(boolean met) {
+        // not supported on this network
+    }
 }
index 32d77a1..120b228 100644 (file)
@@ -17,8 +17,8 @@
 package android.net.wifi;
 
 import com.android.internal.util.AsyncChannel;
-import com.android.internal.util.HierarchicalState;
-import com.android.internal.util.HierarchicalStateMachine;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
 
 import android.content.Context;
 import android.content.Intent;
@@ -46,7 +46,7 @@ import android.util.Log;
  * reloads the configuration and updates the IP and proxy
  * settings, if any.
  */
-class WpsStateMachine extends HierarchicalStateMachine {
+class WpsStateMachine extends StateMachine {
 
     private static final String TAG = "WpsStateMachine";
     private static final boolean DBG = false;
@@ -58,9 +58,9 @@ class WpsStateMachine extends HierarchicalStateMachine {
     private Context mContext;
     AsyncChannel mReplyChannel = new AsyncChannel();
 
-    private HierarchicalState mDefaultState = new DefaultState();
-    private HierarchicalState mInactiveState = new InactiveState();
-    private HierarchicalState mActiveState = new ActiveState();
+    private State mDefaultState = new DefaultState();
+    private State mInactiveState = new InactiveState();
+    private State mActiveState = new ActiveState();
 
     public WpsStateMachine(Context context, WifiStateMachine wsm, Handler target) {
         super(TAG, target.getLooper());
@@ -82,7 +82,7 @@ class WpsStateMachine extends HierarchicalStateMachine {
      * HSM states
      *******************************************************/
 
-    class DefaultState extends HierarchicalState {
+    class DefaultState extends State {
         @Override
          public void enter() {
              if (DBG) Log.d(TAG, getName() + "\n");
@@ -128,7 +128,7 @@ class WpsStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class ActiveState extends HierarchicalState {
+    class ActiveState extends State {
         @Override
          public void enter() {
              if (DBG) Log.d(TAG, getName() + "\n");
@@ -182,7 +182,7 @@ class WpsStateMachine extends HierarchicalStateMachine {
         }
     }
 
-    class InactiveState extends HierarchicalState {
+    class InactiveState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");