diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml
index 7b3fabd34..a26bb8880 100644
--- a/samples/ApiDemos/AndroidManifest.xml
+++ b/samples/ApiDemos/AndroidManifest.xml
@@ -984,6 +984,18 @@
+
+
+
+
+
+
+
+
+
diff --git a/samples/ApiDemos/assets/HelloActivity.apk b/samples/ApiDemos/assets/HelloActivity.apk
new file mode 100644
index 000000000..4e4b4875a
Binary files /dev/null and b/samples/ApiDemos/assets/HelloActivity.apk differ
diff --git a/samples/ApiDemos/res/layout/install_apk.xml b/samples/ApiDemos/res/layout/install_apk.xml
new file mode 100644
index 000000000..1482d2c3a
--- /dev/null
+++ b/samples/ApiDemos/res/layout/install_apk.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ApiDemos/res/values/strings.xml b/samples/ApiDemos/res/values/strings.xml
index ddf7e39f5..45d47554f 100644
--- a/samples/ApiDemos/res/values/strings.xml
+++ b/samples/ApiDemos/res/values/strings.xml
@@ -363,6 +363,8 @@
Pick a Phone
Pick an Address
+ Content/Packages/Install Apk
+
diff --git a/samples/ApiDemos/src/com/example/android/apis/content/FileProvider.java b/samples/ApiDemos/src/com/example/android/apis/content/FileProvider.java
new file mode 100644
index 000000000..156625a67
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/content/FileProvider.java
@@ -0,0 +1,115 @@
+/*
+ * 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.example.android.apis.content;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.ContentProvider.PipeDataWriter;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+/**
+ * A very simple content provider that can serve arbitrary asset files from
+ * our .apk.
+ */
+public class FileProvider extends ContentProvider
+ implements PipeDataWriter {
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ // Don't support queries.
+ return null;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ // Don't support inserts.
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ // Don't support deletes.
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ // Don't support updates.
+ return 0;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ // For this sample, assume all files are .apks.
+ return "application/vnd.android.package-archive";
+ }
+
+ @Override
+ public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
+ // Try to open an asset with the given name.
+ try {
+ InputStream is = getContext().getAssets().open(uri.getPath());
+ // Start a new thread that pipes the stream data back to the caller.
+ return new AssetFileDescriptor(
+ openPipeHelper(uri, null, null, is, this), 0,
+ AssetFileDescriptor.UNKNOWN_LENGTH);
+ } catch (IOException e) {
+ FileNotFoundException fnf = new FileNotFoundException("Unable to open " + uri);
+ throw fnf;
+ }
+ }
+
+ @Override
+ public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType,
+ Bundle opts, InputStream args) {
+ // Transfer data from the asset to the pipe the client is reading.
+ byte[] buffer = new byte[8192];
+ int n;
+ FileOutputStream fout = new FileOutputStream(output.getFileDescriptor());
+ try {
+ while ((n=args.read(buffer)) >= 0) {
+ fout.write(buffer, 0, n);
+ }
+ } catch (IOException e) {
+ Log.i("InstallApk", "Failed transferring", e);
+ } finally {
+ try {
+ args.close();
+ } catch (IOException e) {
+ }
+ try {
+ fout.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/content/InstallApk.java b/samples/ApiDemos/src/com/example/android/apis/content/InstallApk.java
new file mode 100644
index 000000000..9036ee031
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/content/InstallApk.java
@@ -0,0 +1,179 @@
+/*
+ * 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.example.android.apis.content;
+
+// Need the following import to get access to the app resources, since this
+// class is in a sub-package.
+import com.example.android.apis.R;
+
+import android.app.Activity;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ContentProvider.PipeDataWriter;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+
+/**
+ * Demonstration of styled text resources.
+ */
+public class InstallApk extends Activity {
+ static final int REQUEST_INSTALL = 1;
+ static final int REQUEST_UNINSTALL = 2;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.install_apk);
+
+ // Watch for button clicks.
+ Button button = (Button)findViewById(R.id.unknown_source);
+ button.setOnClickListener(mUnknownSourceListener);
+ button = (Button)findViewById(R.id.my_source);
+ button.setOnClickListener(mMySourceListener);
+ button = (Button)findViewById(R.id.replace);
+ button.setOnClickListener(mReplaceListener);
+ button = (Button)findViewById(R.id.uninstall);
+ button.setOnClickListener(mUninstallListener);
+ button = (Button)findViewById(R.id.uninstall_result);
+ button.setOnClickListener(mUninstallResultListener);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent intent) {
+ if (requestCode == REQUEST_INSTALL) {
+ if (resultCode == Activity.RESULT_OK) {
+ Toast.makeText(this, "Install succeeded!", Toast.LENGTH_SHORT).show();
+ } else if (resultCode == Activity.RESULT_CANCELED) {
+ Toast.makeText(this, "Install canceled!", Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(this, "Install Failed!", Toast.LENGTH_SHORT).show();
+ }
+ } else if (requestCode == REQUEST_UNINSTALL) {
+ if (resultCode == Activity.RESULT_OK) {
+ Toast.makeText(this, "Uninstall succeeded!", Toast.LENGTH_SHORT).show();
+ } else if (resultCode == Activity.RESULT_CANCELED) {
+ Toast.makeText(this, "Uninstall canceled!", Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(this, "Uninstall Failed!", Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+ private OnClickListener mUnknownSourceListener = new OnClickListener() {
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
+ intent.setData(Uri.fromFile(prepareApk("HelloActivity.apk")));
+ startActivity(intent);
+ }
+ };
+
+ private OnClickListener mMySourceListener = new OnClickListener() {
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
+ intent.setData(Uri.fromFile(prepareApk("HelloActivity.apk")));
+ intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
+ intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
+ intent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
+ getApplicationInfo().packageName);
+ startActivityForResult(intent, REQUEST_INSTALL);
+ }
+ };
+
+ private OnClickListener mReplaceListener = new OnClickListener() {
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
+ intent.setData(Uri.fromFile(prepareApk("HelloActivity.apk")));
+ intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
+ intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
+ intent.putExtra(Intent.EXTRA_ALLOW_REPLACE, true);
+ intent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
+ getApplicationInfo().packageName);
+ startActivityForResult(intent, REQUEST_INSTALL);
+ }
+ };
+
+ private OnClickListener mUninstallListener = new OnClickListener() {
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
+ intent.setData(Uri.parse(
+ "package:com.example.android.helloactivity"));
+ startActivity(intent);
+ }
+ };
+
+ private OnClickListener mUninstallResultListener = new OnClickListener() {
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
+ intent.setData(Uri.parse(
+ "package:com.example.android.helloactivity"));
+ intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
+ startActivityForResult(intent, REQUEST_UNINSTALL);
+ }
+ };
+
+ private File prepareApk(String assetName) {
+ // Copy the given asset out into a file so that it can be installed.
+ // Returns the path to the file.
+ byte[] buffer = new byte[8192];
+ InputStream is = null;
+ FileOutputStream fout = null;
+ try {
+ is = getAssets().open(assetName);
+ fout = openFileOutput("tmp.apk", Context.MODE_WORLD_READABLE);
+ int n;
+ while ((n=is.read(buffer)) >= 0) {
+ fout.write(buffer, 0, n);
+ }
+ } catch (IOException e) {
+ Log.i("InstallApk", "Failed transferring", e);
+ } finally {
+ try {
+ if (is != null) {
+ is.close();
+ }
+ } catch (IOException e) {
+ }
+ try {
+ if (fout != null) {
+ fout.close();
+ }
+ } catch (IOException e) {
+ }
+ }
+
+ return getFileStreamPath("tmp.apk");
+ }
+}