am a29f43f6: am 1a2ddb42: sdk doc change: Added KeyChain API Demo

* commit 'a29f43f6e18ef78e73b64e6f2a0b34908f67fa21':
  sdk doc change: Added KeyChain API Demo
This commit is contained in:
Tony Chan
2012-03-31 02:20:36 -07:00
committed by Android Git Automerger
14 changed files with 836 additions and 0 deletions

View File

@@ -170,6 +170,7 @@ development/samples/CubeLiveWallpaper samples/${PLATFORM_NAME}/CubeLive
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

View File

@@ -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))

View File

@@ -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>

View File

@@ -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" />

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -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>

View File

@@ -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>

View File

@@ -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);
}
});
}
}

View File

@@ -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 "";
}
}

View File

@@ -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);
}
}