Sample app to show how to integrate an IMS framework into Android.

This sample app demonstrates the events and APIs that are available for
third parties to use to integrate an IMS stack into Android for SMS.
Voice calls over IMS are not yet supported.

Change-Id: Ic1caa3e04e3e3c0d9a896c2d59c46057e52ef712
diff --git a/ExampleImsFramework/Android.mk b/ExampleImsFramework/Android.mk
new file mode 100644
index 0000000..2c2a60d
--- /dev/null
+++ b/ExampleImsFramework/Android.mk
@@ -0,0 +1,22 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_CERTIFICATE := platform
+LOCAL_PACKAGE_NAME := ExampleImsFramework
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+include $(BUILD_PACKAGE)
diff --git a/ExampleImsFramework/AndroidManifest.xml b/ExampleImsFramework/AndroidManifest.xml
new file mode 100644
index 0000000..3b049a3
--- /dev/null
+++ b/ExampleImsFramework/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.example.imsframework"
+        android:sharedUserId="android.uid.phone" >
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
+
+    <application android:name="ImsFrameworkApp"
+                 android:persistent="true"
+                 android:label="@string/app_label"
+                 android:process="com.android.phone">
+
+        <receiver android:name="ImsFrameworkReceiver">
+            <intent-filter>
+                <action android:name="android.intent.action.BOOT_COMPLETED"/>
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.ACTION_SHUTDOWN"/>
+            </intent-filter>
+        </receiver>
+
+        <service android:name="ImsFrameworkService" android:exported="false" />
+
+    </application>
+
+</manifest>
diff --git a/ExampleImsFramework/README b/ExampleImsFramework/README
new file mode 100644
index 0000000..4e957c6
--- /dev/null
+++ b/ExampleImsFramework/README
@@ -0,0 +1,5 @@
+ExampleImsFramework.apk:
+
+Demonstrates how to implement an IMS framework on top of Android.
+
+Owner: Jake Hamby <jhamby@google.com>
diff --git a/ExampleImsFramework/res/values/strings.xml b/ExampleImsFramework/res/values/strings.xml
new file mode 100644
index 0000000..dd4fd71
--- /dev/null
+++ b/ExampleImsFramework/res/values/strings.xml
@@ -0,0 +1,24 @@
+<?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.
+ */
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- App name. Do not translate. -->
+    <string name="app_label">Example IMS Framework</string>
+
+</resources>
diff --git a/ExampleImsFramework/src/com/android/example/imsframework/ImsFrameworkApp.java b/ExampleImsFramework/src/com/android/example/imsframework/ImsFrameworkApp.java
new file mode 100644
index 0000000..414e90f
--- /dev/null
+++ b/ExampleImsFramework/src/com/android/example/imsframework/ImsFrameworkApp.java
@@ -0,0 +1,136 @@
+/*
+ * 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.example.imsframework;
+
+import android.app.Application;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyIntents;
+
+import java.util.Arrays;
+
+/**
+ * Top-level Application class for the example IMS framework.
+ */
+public class ImsFrameworkApp extends Application {
+    private static final String TAG = "ImsFrameworkApp";
+
+    // Broadcast receiver for telephony intent broadcasts
+    private final BroadcastReceiver mReceiver = new ImsFrameworkBroadcastReceiver();
+
+    // Handler for ISIM authentication callback
+    private final IsimAuthenticationHandler mHandler = new IsimAuthenticationHandler();
+
+    private static final int EVENT_ISIM_AUTHENTICATION_DONE = 100;
+
+    @Override
+    public void onCreate() {
+        // Register for telephony intent broadcasts
+        Log.d(TAG, "onCreate(): registering for telephony state change broadcasts");
+        IntentFilter intentFilter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        intentFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
+        intentFilter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        intentFilter.addAction(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED);
+        intentFilter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+        registerReceiver(mReceiver, intentFilter);
+    }
+
+    /**
+     * Receiver for telephony broadcasts that the IMS framework cares about.
+     */
+    private class ImsFrameworkBroadcastReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            Log.d(TAG, "mReceiver received action " + action);
+            if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
+                Log.d(TAG, "mReceiver: ACTION_AIRPLANE_MODE_CHANGED");
+                Log.d(TAG, "- state: " + intent.getBooleanExtra(Phone.STATE_KEY, false));
+            } else if (action.equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
+                Log.d(TAG, "mReceiver: ACTION_ANY_DATA_CONNECTION_STATE_CHANGED");
+                Log.d(TAG, "- apnName: " + intent.getStringExtra(Phone.DATA_APN_KEY));
+                Log.d(TAG, "- apnType: " + intent.getStringExtra(Phone.DATA_APN_TYPE_KEY));
+                Log.d(TAG, "- state: " + intent.getStringExtra(Phone.STATE_KEY));
+                Log.d(TAG, "- reason: " + intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY));
+                Log.d(TAG, "- network unavailable: " +
+                        intent.getBooleanExtra(Phone.NETWORK_UNAVAILABLE_KEY, false));
+                // demonstrate calling ISIM authentication when data state changes
+                tryIsimAuthentication();
+            } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
+                Log.d(TAG, "mReceiver: ACTION_SIM_STATE_CHANGED");
+                Log.d(TAG, "- phoneName: " + intent.getStringExtra(Phone.PHONE_NAME_KEY));
+                String state = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE);
+                Log.d(TAG, "- state: " + state);
+                Log.d(TAG, "- reason: " + intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY));
+                if (IccCard.INTENT_VALUE_ICC_LOADED.equals(state)) {
+                    // all ISIM records are loaded, query them through TelephonyManager
+                    handleSimRecordsLoaded();
+                }
+            } else if (action.equals(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED)) {
+                Log.d(TAG, "mReceiver: ACTION_RADIO_TECHNOLOGY_CHANGED");
+                Log.d(TAG, "- phoneName: " + intent.getStringExtra(Phone.PHONE_NAME_KEY));
+            } else if (action.equals(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED)) {
+                Log.d(TAG, "mReceiver: ACTION_SERVICE_STATE_CHANGED");
+                ServiceState ss = ServiceState.newFromBundle(intent.getExtras());
+                Log.d(TAG, "- ServiceState: " + ss);
+            }
+        }
+    }
+
+    void handleSimRecordsLoaded() {
+        TelephonyManager telephonyManager =
+                (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
+
+        Log.d(TAG, "ISIM IMPI: " + telephonyManager.getIsimImpi());
+        Log.d(TAG, "ISIM Domain: " + telephonyManager.getIsimDomain());
+        Log.d(TAG, "ISIM IMPU: " + Arrays.toString(telephonyManager.getIsimImpu()));
+    }
+
+    private final class IsimAuthenticationHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            AsyncResult ar = (AsyncResult) msg.obj;
+            if (ar.exception != null) {
+                Log.d(TAG, "requestIsimAuthentication exception: " + ar.exception);
+            } else {
+                String response = (String) ar.result;
+                Log.d(TAG, "requestIsimAuthentication response: " + response);
+            }
+        }
+    }
+
+    void tryIsimAuthentication() {
+        Message response = mHandler.obtainMessage(EVENT_ISIM_AUTHENTICATION_DONE);
+        // Note: this only works when running inside the phone process
+        Phone phone = PhoneFactory.getDefaultPhone();
+        if (phone != null) {
+            phone.requestIsimAuthentication("DUMMY-BASE64-NONCE", response);
+        }
+    }
+}
diff --git a/ExampleImsFramework/src/com/android/example/imsframework/ImsFrameworkReceiver.java b/ExampleImsFramework/src/com/android/example/imsframework/ImsFrameworkReceiver.java
new file mode 100644
index 0000000..5648f32
--- /dev/null
+++ b/ExampleImsFramework/src/com/android/example/imsframework/ImsFrameworkReceiver.java
@@ -0,0 +1,44 @@
+/*
+ * 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.example.imsframework;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class ImsFrameworkReceiver extends BroadcastReceiver {
+    private static final String TAG = "ImsFrameworkReceiver";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
+        if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
+            Log.d(TAG, "starting service for ACTION_BOOT_COMPLETED");
+            intent = new Intent(intent);
+            intent.setClass(context, ImsFrameworkService.class);
+            if (context.startService(intent) == null) {
+                Log.e(TAG, "Can't start service");
+            }
+        } else if (Intent.ACTION_SHUTDOWN.equals(action)) {
+            Log.d(TAG, "received ACTION_SHUTDOWN, shutting down IMS");
+            // System is shutting down immediately: perform cleanup here
+        } else {
+            Log.e(TAG, "Received unknown intent: " + action);
+        }
+    }
+}
diff --git a/ExampleImsFramework/src/com/android/example/imsframework/ImsFrameworkService.java b/ExampleImsFramework/src/com/android/example/imsframework/ImsFrameworkService.java
new file mode 100644
index 0000000..8dec70d
--- /dev/null
+++ b/ExampleImsFramework/src/com/android/example/imsframework/ImsFrameworkService.java
@@ -0,0 +1,56 @@
+/*
+ * 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.example.imsframework;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+/**
+ * Example service to handle IMS setup after boot completed event.
+ *
+ * Setting {@code android:persistent="true"} in the manifest will cause
+ * {@link ImsFrameworkApp#onCreate()} to be called at system startup,
+ * before {@link Intent#ACTION_BOOT_COMPLETED} is broadcast, so early
+ * initialization can be performed there, such as registering to receive
+ * telephony state change broadcasts that can't be declared in the manifest.
+ */
+public class ImsFrameworkService extends Service {
+    private static final String TAG = "ImsFrameworkService";
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        String action = intent.getAction();
+        Log.d(TAG, "Service starting for intent " + action);
+        if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
+            Log.d(TAG, "Received ACTION_BOOT_COMPLETED");
+            handleBootCompleted();
+        }
+        stopSelf();     // stop service after handling the action
+        return START_NOT_STICKY;
+    }
+
+    private void handleBootCompleted() {
+        // Code to execute after boot completes, e.g. connecting to the IMS PDN
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;    // clients can't bind to this service
+    }
+}