diff options
| author | Peter Visontay <pvisontay@google.com> | 2017-11-16 19:58:57 +0000 |
|---|---|---|
| committer | Peter Visontay <pvisontay@google.com> | 2017-11-16 21:49:17 +0000 |
| commit | 75bca25be11cd5612bd071579795d8ba840f4a68 (patch) | |
| tree | ac55be3c734c26c6e3f1f46e0f6d9af70dac6f15 /samples/ApiDemos | |
| parent | 656dd61384274b7b4e25e32b17236df13592acbb (diff) | |
| download | android_development-75bca25be11cd5612bd071579795d8ba840f4a68.tar.gz android_development-75bca25be11cd5612bd071579795d8ba840f4a68.tar.bz2 android_development-75bca25be11cd5612bd071579795d8ba840f4a68.zip | |
Add a demo of the Session-based PackageInstaller to ApiDemos.
Test: Manually.
Change-Id: If084477e8970777ef5cd90c55afe86a116ee1458
Diffstat (limited to 'samples/ApiDemos')
6 files changed, 198 insertions, 4 deletions
diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml index d9744047a..df4232dd1 100644 --- a/samples/ApiDemos/AndroidManifest.xml +++ b/samples/ApiDemos/AndroidManifest.xml @@ -39,7 +39,8 @@ <uses-permission android:name="android.permission.TRANSMIT_IR" /> <uses-permission android:name="android.permission.READ_PHONE_STATE"/> <uses-permission android:name="android.permission.WAKE_LOCK"/> - + <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/> + <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES"/> <!-- For android.media.audiofx.Visualizer --> <uses-permission android:name="android.permission.RECORD_AUDIO" /> @@ -1310,6 +1311,19 @@ </intent-filter> </activity> + <activity android:name=".content.InstallApkSessionApi" + android:label="@string/activity_install_apk_session_api" + android:launchMode="singleTop"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.SAMPLE_CODE" /> + </intent-filter> + <intent-filter> + <action android:exported="true" + android:name="com.example.android.apis.content.SESSION_API_PACKAGE_INSTALLED" /> + </intent-filter> + </activity> + <activity android:name=".content.DocumentsSample" android:label="@string/activity_documents" android:enabled="@bool/atLeastKitKat"> diff --git a/samples/ApiDemos/res/layout/install_apk.xml b/samples/ApiDemos/res/layout/install_apk.xml index 1482d2c3a..8a1dbf28f 100644 --- a/samples/ApiDemos/res/layout/install_apk.xml +++ b/samples/ApiDemos/res/layout/install_apk.xml @@ -14,8 +14,8 @@ limitations under the License. --> -<!-- Demonstrates starting and stopping a local service. - See corresponding Java code com.android.sdk.app.LocalSerice.java. --> + +<!-- Demonstrates installing and uninstalling packages. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:padding="4dip" android:gravity="center_horizontal" diff --git a/samples/ApiDemos/res/layout/install_apk_session_api.xml b/samples/ApiDemos/res/layout/install_apk_session_api.xml new file mode 100644 index 000000000..b3422f153 --- /dev/null +++ b/samples/ApiDemos/res/layout/install_apk_session_api.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 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. +--> + +<!-- Demonstrates installing and uninstalling packages using the Session API. --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:padding="4dip" + android:gravity="center_horizontal" + android:layout_width="match_parent" android:layout_height="match_parent"> + + <TextView + android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_weight="0" + android:paddingBottom="4dip" + android:textAppearance="?android:attr/textAppearanceMedium" + android:text="Demonstrates using the PackageInstaller's Session API to install an application."/> + + <Button android:id="@+id/install" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="Install"> + </Button> + +</LinearLayout> diff --git a/samples/ApiDemos/res/values/strings.xml b/samples/ApiDemos/res/values/strings.xml index 81df88925..2a7d639d9 100644 --- a/samples/ApiDemos/res/values/strings.xml +++ b/samples/ApiDemos/res/values/strings.xml @@ -492,6 +492,7 @@ <string name="activity_media_content_observer">Content/Provider/Media Content Observer</string> <string name="activity_install_apk">Content/Packages/Install Apk</string> + <string name="activity_install_apk_session_api">Content/Packages/Install Apk using Session API</string> <string name="ir_send">Send IR</string> <string name="ir_get_freqs">Get Carrier Frequencies</string> diff --git a/samples/ApiDemos/src/com/example/android/apis/content/InstallApk.java b/samples/ApiDemos/src/com/example/android/apis/content/InstallApk.java index 9036ee031..41194ea9b 100644 --- a/samples/ApiDemos/src/com/example/android/apis/content/InstallApk.java +++ b/samples/ApiDemos/src/com/example/android/apis/content/InstallApk.java @@ -46,7 +46,10 @@ import java.io.InputStream; /** - * Demonstration of styled text resources. + * Demonstration of package installation and uninstallation using the original (non-Session) + * package installation API that uses {@link Intent#ACTION_INSTALL_PACKAGE}. + * + * @see InstallApkSessionApi for a demo of the newer Session API. */ public class InstallApk extends Activity { static final int REQUEST_INSTALL = 1; diff --git a/samples/ApiDemos/src/com/example/android/apis/content/InstallApkSessionApi.java b/samples/ApiDemos/src/com/example/android/apis/content/InstallApkSessionApi.java new file mode 100644 index 000000000..cd7915fbb --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/content/InstallApkSessionApi.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2017 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.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.IntentSender; +import android.content.pm.PackageInstaller; +import android.os.Bundle; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.Toast; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + + +/** + * Demonstration of package installation and uninstallation using the package installer Session + * API. + * + * @see InstallApk for a demo of the original (non-Session) API. + */ +public class InstallApkSessionApi extends Activity { + private static final String PACKAGE_INSTALLED_ACTION = + "com.example.android.apis.content.SESSION_API_PACKAGE_INSTALLED"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.install_apk_session_api); + + // Watch for button clicks. + Button button = (Button) findViewById(R.id.install); + button.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + PackageInstaller.Session session = null; + try { + PackageInstaller packageInstaller = getPackageManager().getPackageInstaller(); + PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( + PackageInstaller.SessionParams.MODE_FULL_INSTALL); + int sessionId = packageInstaller.createSession(params); + session = packageInstaller.openSession(sessionId); + + addApkToInstallSession("HelloActivity.apk", session); + + // Create an install status receiver. + Context context = InstallApkSessionApi.this; + Intent intent = new Intent(context, InstallApkSessionApi.class); + intent.setAction(PACKAGE_INSTALLED_ACTION); + PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0); + IntentSender statusReceiver = pendingIntent.getIntentSender(); + + // Commit the session (this will start the installation workflow). + session.commit(statusReceiver); + } catch (IOException e) { + throw new RuntimeException("Couldn't install package", e); + } catch (RuntimeException e) { + if (session != null) { + session.abandon(); + } + throw e; + } + } + }); + } + + private void addApkToInstallSession(String assetName, PackageInstaller.Session session) + throws IOException { + // It's recommended to pass the file size to openWrite(). Otherwise installation may fail + // if the disk is almost full. + try (OutputStream packageInSession = session.openWrite("package", 0, -1); + InputStream is = getAssets().open(assetName)) { + byte[] buffer = new byte[16384]; + int n; + while ((n = is.read(buffer)) >= 0) { + packageInSession.write(buffer, 0, n); + } + } + } + + // Note: this Activity must run in singleTop launchMode for it to be able to receive the intent + // in onNewIntent(). + @Override + protected void onNewIntent(Intent intent) { + Bundle extras = intent.getExtras(); + if (PACKAGE_INSTALLED_ACTION.equals(intent.getAction())) { + int status = extras.getInt(PackageInstaller.EXTRA_STATUS); + String message = extras.getString(PackageInstaller.EXTRA_STATUS_MESSAGE); + + switch (status) { + case PackageInstaller.STATUS_PENDING_USER_ACTION: + // This test app isn't privileged, so the user has to confirm the install. + Intent confirmIntent = (Intent) extras.get(Intent.EXTRA_INTENT); + startActivity(confirmIntent); + break; + + case PackageInstaller.STATUS_SUCCESS: + Toast.makeText(this, "Install succeeded!", Toast.LENGTH_SHORT).show(); + break; + + case PackageInstaller.STATUS_FAILURE: + case PackageInstaller.STATUS_FAILURE_ABORTED: + case PackageInstaller.STATUS_FAILURE_BLOCKED: + case PackageInstaller.STATUS_FAILURE_CONFLICT: + case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE: + case PackageInstaller.STATUS_FAILURE_INVALID: + case PackageInstaller.STATUS_FAILURE_STORAGE: + Toast.makeText(this, "Install failed! " + status + ", " + message, + Toast.LENGTH_SHORT).show(); + break; + default: + Toast.makeText(this, "Unrecognized status received from installer: " + status, + Toast.LENGTH_SHORT).show(); + } + } + } +} |
