sdk doc change: Added KeyChain API Demo

Change-Id: Ib2b751a9de485359705b243d2c70e5f8f2c8ab3c
diff --git a/build/sdk.atree b/build/sdk.atree
index 038dafc..b00aa64 100644
--- a/build/sdk.atree
+++ b/build/sdk.atree
@@ -170,6 +170,7 @@
 development/samples/Home                       samples/${PLATFORM_NAME}/Home
 development/samples/HoneycombGallery           samples/${PLATFORM_NAME}/HoneycombGallery
 development/samples/JetBoy                     samples/${PLATFORM_NAME}/JetBoy
+development/samples/KeyChainDemo               samples/${PLATFORM_NAME}/KeyChainDemo
 development/samples/LunarLander                samples/${PLATFORM_NAME}/LunarLander
 development/samples/MultiResolution            samples/${PLATFORM_NAME}/MultiResolution
 development/samples/NotePad                    samples/${PLATFORM_NAME}/NotePad
diff --git a/samples/KeyChainDemo/Android.mk b/samples/KeyChainDemo/Android.mk
new file mode 100644
index 0000000..a607e95
--- /dev/null
+++ b/samples/KeyChainDemo/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := KeyChainDemo
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/samples/KeyChainDemo/AndroidManifest.xml b/samples/KeyChainDemo/AndroidManifest.xml
new file mode 100644
index 0000000..e529309
--- /dev/null
+++ b/samples/KeyChainDemo/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2012 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.example.android.keychain"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="15" />
+
+    <uses-permission android:name="android.permission.INTERNET" />
+
+    <application
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name" >
+        <activity
+            android:name="com.example.android.keychain.KeyChainDemoActivity"
+            android:label="@string/app_name"
+            android:launchMode="singleTop"
+            android:screenOrientation="portrait" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <service android:name="com.example.android.keychain.SecureWebServerService" />
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/samples/KeyChainDemo/_index.html b/samples/KeyChainDemo/_index.html
new file mode 100644
index 0000000..57e91b3
--- /dev/null
+++ b/samples/KeyChainDemo/_index.html
@@ -0,0 +1,50 @@
+<p>This is a demo application highlighting how to use the KeyChain APIs introduced in API Level 14.</p>
+
+<p>The source code for this demo app shows how to install a PKCS12 key chain stored in the assets folder,
+ grant permission to the app to use the installed key chain and finally display the certificate and
+ private key info.  The app also has a simple implementation of a web server listening to requests
+ at an SSL socket using the same certificate in the key chain.
+</p>
+
+<p>The application includes the following key classes:</p>
+<ul>
+  <li><a href="src/com/example/android/keychain/KeyChainDemoActivity.html"><code>KeyChainDemoActivity</code></a>
+    &mdash; the main <code>Activity</code> that is used to install the key chain and start/stop
+    the web server.  </li>
+  <li><a href="src/com/example/android/keychain/SecureWebServer.html"><code>SecureWebServer</code></a>
+    &mdash; a single thread web server listening at port 8080 for <code>https://localhost:8080</code> request</li>
+  <li><a href="src/com/example/android/keychain/SecureWebServerService.html"><code>SecureWebServerService</code></a>
+    &mdash; a <code>Service</code> that runs the web server in the foreground.</li>
+</ul>
+
+<p>If you are developing an application that uses the KeyChain APIs,
+ remember that the feature is supported only on Android 4.0 (API level 14) and
+ higher versions of the platform. To ensure that your application can only be
+ installed on devices that are running Android 4.0, remember to add the
+ following to the application's manifest:</p>
+<ul>
+  <li><code>&lt;uses-sdk android:minSdkVersion="14" /&gt;</code>, which
+    indicates to the Android platform that your application requires
+    Android 4.0 or higher. For more information, see <a
+    href="../../../guide/appendix/api-levels.html">API Levels</a> and the
+    documentation for the <a
+    href="../../../guide/topics/manifest/uses-sdk-element.html"><code>&lt;uses-sdk&gt;</code></a>
+    element.</li>
+</ul>
+
+<p>Note: Due to browser cache, you need to restart the browser completely before it can recognize
+ any keystore updates.  The easiest way to do this is to dismiss the app in the Recent Apps list.
+</p>
+
+<p>
+  For more information about using the KeyChain API, see the
+  <a href="../../../reference/android/security/KeyChain.html">
+  <code>android.security.KeyChain</code></a>
+  documentation.
+</p>
+
+<img alt="" src="../images/KeyChainDemo1.png" />
+<img alt="" src="../images/KeyChainDemo2.png" />
+<img alt="" src="../images/KeyChainDemo3.png" />
+<img alt="" src="../images/KeyChainDemo4.png" />
+
diff --git a/samples/KeyChainDemo/assets/keychain.p12 b/samples/KeyChainDemo/assets/keychain.p12
new file mode 100644
index 0000000..66bd916
--- /dev/null
+++ b/samples/KeyChainDemo/assets/keychain.p12
Binary files differ
diff --git a/samples/KeyChainDemo/assets/training-prof.png b/samples/KeyChainDemo/assets/training-prof.png
new file mode 100644
index 0000000..ad91db2
--- /dev/null
+++ b/samples/KeyChainDemo/assets/training-prof.png
Binary files differ
diff --git a/samples/KeyChainDemo/res/drawable-hdpi/ic_launcher.png b/samples/KeyChainDemo/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..8074c4c
--- /dev/null
+++ b/samples/KeyChainDemo/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/KeyChainDemo/res/drawable-ldpi/ic_launcher.png b/samples/KeyChainDemo/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..1095584
--- /dev/null
+++ b/samples/KeyChainDemo/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/samples/KeyChainDemo/res/drawable-mdpi/ic_launcher.png b/samples/KeyChainDemo/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..a07c69f
--- /dev/null
+++ b/samples/KeyChainDemo/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/KeyChainDemo/res/layout/main.xml b/samples/KeyChainDemo/res/layout/main.xml
new file mode 100644
index 0000000..c968c08
--- /dev/null
+++ b/samples/KeyChainDemo/res/layout/main.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2012 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="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical" >
+
+    <TextView
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="5dp"
+        android:text="@string/keychain_label" />
+
+    <Button
+        android:id="@+id/keychain_button"
+        android:layout_width="100dp"
+        android:layout_height="wrap_content"
+        android:layout_margin="5dp"
+        android:text="@string/keychain_install" />
+
+    <TextView
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="5dp"
+        android:text="@string/server_label" />
+
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <Button
+            android:id="@+id/server_button"
+            android:layout_width="100dp"
+            android:layout_height="wrap_content"
+            android:layout_margin="5dp"
+            android:text="@string/server_start" />
+
+        <Button
+            android:id="@+id/test_ssl_button"
+            android:layout_width="100dp"
+            android:layout_height="wrap_content"
+            android:layout_margin="5dp"
+            android:text="@string/test_ssl" />
+    </LinearLayout>
+
+    <TextView
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="5dp"
+        android:text="@string/cert_label" />
+
+    <TextView
+        android:id="@+id/cert"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="5dp"
+        android:gravity="left" />
+
+    <TextView
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="5dp"
+        android:text="@string/private_key_label" />
+
+    <ScrollView
+        android:layout_width="fill_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1" >
+
+        <TextView
+            android:id="@+id/private_key"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_margin="5dp"
+            android:gravity="left" />
+    </ScrollView>
+
+</LinearLayout>
diff --git a/samples/KeyChainDemo/res/values/strings.xml b/samples/KeyChainDemo/res/values/strings.xml
new file mode 100644
index 0000000..748f698
--- /dev/null
+++ b/samples/KeyChainDemo/res/values/strings.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2012 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>
+
+    <string name="app_name">KeyChainDemo</string>
+    <string name="cert_label">Certificate</string>
+    <string name="private_key_label">Private Key</string>
+    <string name="keychain_label">Key Chain Installation</string>
+    <string name="keychain_install">Install</string>
+    <string name="keychain_installed">Installed</string>
+    <string name="server_label">Simple SSL Web Server</string>
+    <string name="server_start">Start</string>
+    <string name="server_stop">Stop</string>
+    <string name="test_ssl">Test</string>
+    <string name="ticker_text">Starting KeyChainDemo SSL Server</string>
+    <string name="notification_title">KeyChainDemo SSL Server</string>
+    <string name="notification_message">Click to stop the server</string>
+
+</resources>
\ No newline at end of file
diff --git a/samples/KeyChainDemo/src/com/example/android/keychain/KeyChainDemoActivity.java b/samples/KeyChainDemo/src/com/example/android/keychain/KeyChainDemoActivity.java
new file mode 100644
index 0000000..a2028b8
--- /dev/null
+++ b/samples/KeyChainDemo/src/com/example/android/keychain/KeyChainDemoActivity.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright 2012 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.example.android.keychain;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.security.KeyChain;
+import android.security.KeyChainAliasCallback;
+import android.security.KeyChainException;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.TextView;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+public class KeyChainDemoActivity extends Activity implements
+        KeyChainAliasCallback {
+
+    /**
+     * The file name of the PKCS12 file used
+     */
+    public static final String PKCS12_FILENAME = "keychain.p12";
+
+    /**
+     * The pass phrase of the PKCS12 file
+     */
+    public static final String PKCS12_PASSWORD = "changeit";
+
+    /**
+     * Intent extra name to indicate to stop server
+     */
+    public static final String EXTRA_STOP_SERVER = "stop_server";
+
+    // Log tag for this class
+    private static final String TAG = "KeyChainApiActivity";
+
+    // Alias for certificate
+    private static final String DEFAULT_ALIAS = "My Key Chain";
+
+    // Name of the application preference
+    private static final String KEYCHAIN_PREF = "keychain";
+
+    // Name of preference name that saves the alias
+    private static final String KEYCHAIN_PREF_ALIAS = "alias";
+
+    // Request code used when starting the activity using the KeyChain install
+    // intent
+    private static final int INSTALL_KEYCHAIN_CODE = 1;
+
+    // Test SSL URL
+    private static final String TEST_SSL_URL = "https://localhost:8080";
+
+    // Button to start/stop the simple SSL web server
+    private Button serverButton;
+
+    // Button to install the key chain
+    private Button keyChainButton;
+
+    // Button to launch the browser for testing https://localhost:8080
+    private Button testSslButton;
+
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Set the view using the main.xml layout
+        setContentView(R.layout.main);
+
+        // Check whether the key chain is installed or not. This takes time and
+        // should be done in another thread other than the main thread.
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                if (isKeyChainAccessible()) {
+                    // Key chain installed. Disable the install button and print
+                    // the key chain information
+                    disableKeyChainButton();
+                    printInfo();
+                } else {
+                    Log.d(TAG, "Key Chain is not accessible");
+                }
+            }
+        }).start();
+
+        // Setup the key chain installation button
+        keyChainButton = (Button) findViewById(R.id.keychain_button);
+        keyChainButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                installPkcs12();
+            }
+        });
+
+        // Setup the simple SSL web server start/stop button
+        serverButton = (Button) findViewById(R.id.server_button);
+        serverButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (serverButton.getText().equals(
+                        getResources().getString(R.string.server_start))) {
+                    serverButton.setText(R.string.server_stop);
+                    startServer();
+                } else {
+                    serverButton.setText(R.string.server_start);
+                    stopServer();
+                }
+            }
+        });
+
+        // Setup the test SSL page button
+        testSslButton = (Button) findViewById(R.id.test_ssl_button);
+        testSslButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Intent i = new Intent(Intent.ACTION_VIEW, Uri
+                        .parse(TEST_SSL_URL));
+                startActivity(i);
+            }
+        });
+    }
+
+    /**
+     * This will be called when the user click on the notification to stop the
+     * SSL server
+     */
+    @Override
+    protected void onNewIntent(Intent intent) {
+        Log.d(TAG, "In onNewIntent()");
+        super.onNewIntent(intent);
+        boolean isStopServer = intent.getBooleanExtra(EXTRA_STOP_SERVER, false);
+        if (isStopServer) {
+            serverButton.setText(R.string.server_start);
+            stopServer();
+        }
+    }
+
+    /**
+     * This implements the KeyChainAliasCallback
+     */
+    @Override
+    public void alias(String alias) {
+        if (alias != null) {
+            setAlias(alias); // Set the alias in the application preference
+            disableKeyChainButton();
+            printInfo();
+        } else {
+            Log.d(TAG, "User hit Disallow");
+        }
+    }
+
+    /**
+     * This method returns the alias of the key chain from the application
+     * preference
+     *
+     * @return The alias of the key chain
+     */
+    private String getAlias() {
+        SharedPreferences pref = getSharedPreferences(KEYCHAIN_PREF,
+                MODE_PRIVATE);
+        return pref.getString(KEYCHAIN_PREF_ALIAS, DEFAULT_ALIAS);
+    }
+
+    /**
+     * This method sets the alias of the key chain to the application preference
+     */
+    private void setAlias(String alias) {
+        SharedPreferences pref = getSharedPreferences(KEYCHAIN_PREF,
+                MODE_PRIVATE);
+        Editor editor = pref.edit();
+        editor.putString(KEYCHAIN_PREF_ALIAS, alias);
+        editor.commit();
+    }
+
+    /**
+     * This method prints the key chain information.
+     */
+    private void printInfo() {
+        String alias = getAlias();
+        X509Certificate[] certs = getCertificateChain(alias);
+        final PrivateKey privateKey = getPrivateKey(alias);
+        final StringBuffer sb = new StringBuffer();
+        for (X509Certificate cert : certs) {
+            sb.append(cert.getIssuerDN());
+            sb.append("\n");
+        }
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TextView certTv = (TextView) findViewById(R.id.cert);
+                TextView privateKeyTv = (TextView) findViewById(R.id.private_key);
+                certTv.setText(sb.toString());
+                privateKeyTv.setText(privateKey.getFormat() + ":" + privateKey);
+            }
+        });
+    }
+
+    /**
+     * This method will launch an intent to install the key chain
+     */
+    private void installPkcs12() {
+        try {
+            BufferedInputStream bis = new BufferedInputStream(getAssets().open(
+                    PKCS12_FILENAME));
+            byte[] keychain = new byte[bis.available()];
+            bis.read(keychain);
+
+            Intent installIntent = KeyChain.createInstallIntent();
+            installIntent.putExtra(KeyChain.EXTRA_PKCS12, keychain);
+            installIntent.putExtra(KeyChain.EXTRA_NAME, DEFAULT_ALIAS);
+            startActivityForResult(installIntent, INSTALL_KEYCHAIN_CODE);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == INSTALL_KEYCHAIN_CODE) {
+            switch (resultCode) {
+                case Activity.RESULT_OK:
+                    chooseCert();
+                    break;
+                default:
+                    super.onActivityResult(requestCode, resultCode, data);
+            }
+        }
+    }
+
+    private void chooseCert() {
+        KeyChain.choosePrivateKeyAlias(this, this, // Callback
+                new String[] {}, // Any key types.
+                null, // Any issuers.
+                "localhost", // Any host
+                -1, // Any port
+                DEFAULT_ALIAS);
+    }
+
+    private X509Certificate[] getCertificateChain(String alias) {
+        try {
+            return KeyChain.getCertificateChain(this, alias);
+        } catch (KeyChainException e) {
+            e.printStackTrace();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    private PrivateKey getPrivateKey(String alias) {
+        try {
+            return KeyChain.getPrivateKey(this, alias);
+        } catch (KeyChainException e) {
+            e.printStackTrace();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * This method checks if the key chain is installed
+     *
+     * @return true if the key chain is not installed or allowed
+     */
+    private boolean isKeyChainAccessible() {
+        return getCertificateChain(getAlias()) != null
+                && getPrivateKey(getAlias()) != null;
+    }
+
+    /**
+     * This method starts the background service of the simple SSL web server
+     */
+    private void startServer() {
+        Intent secureWebServerIntent = new Intent(this,
+                SecureWebServerService.class);
+        startService(secureWebServerIntent);
+    }
+
+    /**
+     * This method stops the background service of the simple SSL web server
+     */
+    private void stopServer() {
+        Intent secureWebServerIntent = new Intent(this,
+                SecureWebServerService.class);
+        stopService(secureWebServerIntent);
+    }
+
+    /**
+     * This is a convenient method to disable the key chain install button
+     */
+    private void disableKeyChainButton() {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                keyChainButton.setText(R.string.keychain_installed);
+                keyChainButton.setEnabled(false);
+            }
+        });
+    }
+
+}
diff --git a/samples/KeyChainDemo/src/com/example/android/keychain/SecureWebServer.java b/samples/KeyChainDemo/src/com/example/android/keychain/SecureWebServer.java
new file mode 100644
index 0000000..8f84000
--- /dev/null
+++ b/samples/KeyChainDemo/src/com/example/android/keychain/SecureWebServer.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2012 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.example.android.keychain;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.Socket;
+import java.security.KeyStore;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+
+import android.content.Context;
+import android.util.Base64;
+import android.util.Log;
+
+public class SecureWebServer {
+
+    // Log tag for this class
+    private static final String TAG = "SecureWebServer";
+
+    // File name of the image used in server response
+    private static final String EMBEDDED_IMAGE_FILENAME = "training-prof.png";
+
+    private SSLServerSocketFactory sssf;
+    private SSLServerSocket sss;
+
+    // A flag to control whether the web server should be kept running
+    private boolean isRunning = true;
+
+    // The base64 encoded image string used as an embedded image
+    private final String base64Image;
+
+    /**
+     * WebServer constructor.
+     */
+    public SecureWebServer(Context ctx) {
+        try {
+            // Get an SSL context using the TLS protocol
+            SSLContext sslContext = SSLContext.getInstance("TLS");
+
+            // Get a key manager factory using the default algorithm
+            KeyManagerFactory kmf = KeyManagerFactory
+                    .getInstance(KeyManagerFactory.getDefaultAlgorithm());
+
+            // Load the PKCS12 key chain
+            KeyStore ks = KeyStore.getInstance("PKCS12");
+            FileInputStream fis = ctx.getAssets()
+                    .openFd(KeyChainDemoActivity.PKCS12_FILENAME)
+                    .createInputStream();
+            ks.load(fis, KeyChainDemoActivity.PKCS12_PASSWORD.toCharArray());
+            kmf.init(ks, KeyChainDemoActivity.PKCS12_PASSWORD.toCharArray());
+
+            // Initialize the SSL context
+            sslContext.init(kmf.getKeyManagers(), null, null);
+
+            // Create the SSL server socket factory
+            sssf = sslContext.getServerSocketFactory();
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        // Create the base64 image string used in the server response
+        base64Image = createBase64Image(ctx);
+    }
+
+    /**
+     * This method starts the web server listening to the port 8080
+     */
+    protected void start() {
+
+        new Thread(new Runnable() {
+
+            @Override
+            public void run() {
+                Log.d(TAG, "Secure Web Server is starting up on port 8080");
+                try {
+                    // Create the secure server socket
+                    sss = (SSLServerSocket) sssf.createServerSocket(8080);
+                } catch (Exception e) {
+                    System.out.println("Error: " + e);
+                    return;
+                }
+
+                Log.d(TAG, "Waiting for connection");
+                while (isRunning) {
+                    try {
+                        // Wait for an SSL connection
+                        Socket socket = sss.accept();
+
+                        // Got a connection
+                        Log.d(TAG, "Connected, sending data.");
+
+                        BufferedReader in = new BufferedReader(
+                                new InputStreamReader(socket.getInputStream()));
+                        PrintWriter out = new PrintWriter(socket
+                                .getOutputStream());
+
+                        // Read the data until a blank line is reached which
+                        // signifies the end of the client HTTP headers
+                        String str = ".";
+                        while (!str.equals(""))
+                            str = in.readLine();
+
+                        // Send a HTTP response
+                        out.println("HTTP/1.0 200 OK");
+                        out.println("Content-Type: text/html");
+                        out.println("Server: Android KeyChainiDemo SSL Server");
+                        // this blank line signals the end of the headers
+                        out.println("");
+                        // Send the HTML page
+                        out.println("<H1>Welcome to Android!</H1>");
+                        // Add an embedded Android image
+                        out.println("<img src='data:image/png;base64," + base64Image + "'/>");
+                        out.flush();
+                        socket.close();
+                    } catch (Exception e) {
+                        Log.d(TAG, "Error: " + e);
+                    }
+                }
+            }
+        }).start();
+
+    }
+
+    /**
+     * This method stops the SSL web server
+     */
+    protected void stop() {
+        try {
+            // Break out from the infinite while loop in start()
+            isRunning = false;
+
+            // Close the socket
+            if (sss != null) {
+                sss.close();
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * This method reads a binary image from the assets folder and returns the
+     * base64 encoded image string.
+     *
+     * @param ctx The service this web server is running in.
+     * @return String The base64 encoded image string or "" if there is an
+     *         exception
+     */
+    private String createBase64Image(Context ctx) {
+        BufferedInputStream bis;
+        try {
+            bis = new BufferedInputStream(ctx.getAssets().open(EMBEDDED_IMAGE_FILENAME));
+            byte[] embeddedImage = new byte[bis.available()];
+            bis.read(embeddedImage);
+            return Base64.encodeToString(embeddedImage, Base64.DEFAULT);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return "";
+    }
+
+}
diff --git a/samples/KeyChainDemo/src/com/example/android/keychain/SecureWebServerService.java b/samples/KeyChainDemo/src/com/example/android/keychain/SecureWebServerService.java
new file mode 100644
index 0000000..74abb06
--- /dev/null
+++ b/samples/KeyChainDemo/src/com/example/android/keychain/SecureWebServerService.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2012 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.example.android.keychain;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+public class SecureWebServerService extends Service {
+
+    // Log tag for this class
+    private static final String TAG = "SecureWebServerService";
+
+    // A special ID assigned to this on-going notification.
+    private static final int ONGOING_NOTIFICATION = 1248;
+
+    // A handle to the simple SSL web server
+    private SecureWebServer sws;
+
+    /**
+     * Start the SSL web server and set an on-going notification
+     */
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        sws = new SecureWebServer(this);
+        sws.start();
+        createNotification();
+    }
+
+    /**
+     * Stop the SSL web server and remove the on-going notification
+     */
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        sws.stop();
+        stopForeground(true);
+    }
+
+    /**
+     * Return null as there is nothing to bind
+     */
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    /**
+     * Create an on-going notification. It will stop the server when the user
+     * clicks on the notification.
+     */
+    private void createNotification() {
+        Log.d(TAG, "Create an ongoing notification");
+        Intent notificationIntent = new Intent(this,
+                KeyChainDemoActivity.class);
+        notificationIntent.putExtra(KeyChainDemoActivity.EXTRA_STOP_SERVER,
+                true);
+        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
+                notificationIntent, 0);
+        Notification notification = new Notification.Builder(this).
+                setContentTitle(getText(R.string.notification_title)).
+                setContentText(getText(R.string.notification_message)).
+                setSmallIcon(android.R.drawable.ic_media_play).
+                setTicker(getText(R.string.ticker_text)).
+                setOngoing(true).
+                setContentIntent(pendingIntent).
+                getNotification();
+        startForeground(ONGOING_NOTIFICATION, notification);
+    }
+
+}