1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.apis.content;
18 
19 // Need the following import to get access to the app resources, since this
20 // class is in a sub-package.
21 import com.example.android.apis.R;
22 
23 import android.app.Activity;
24 import android.app.PendingIntent;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentSender;
28 import android.content.pm.PackageInstaller;
29 import android.os.Bundle;
30 import android.view.View;
31 import android.view.View.OnClickListener;
32 import android.widget.Button;
33 import android.widget.Toast;
34 
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.io.OutputStream;
38 
39 
40 /**
41  * Demonstration of package installation and uninstallation using the package installer Session
42  * API.
43  *
44  * @see InstallApk for a demo of the original (non-Session) API.
45  */
46 public class InstallApkSessionApi extends Activity {
47     private static final String PACKAGE_INSTALLED_ACTION =
48             "com.example.android.apis.content.SESSION_API_PACKAGE_INSTALLED";
49 
50     @Override
onCreate(Bundle savedInstanceState)51     protected void onCreate(Bundle savedInstanceState) {
52         super.onCreate(savedInstanceState);
53 
54         setContentView(R.layout.install_apk_session_api);
55 
56         // Watch for button clicks.
57         Button button = (Button) findViewById(R.id.install);
58         button.setOnClickListener(new OnClickListener() {
59             public void onClick(View v) {
60                 PackageInstaller.Session session = null;
61                 try {
62                     PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
63                     PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
64                             PackageInstaller.SessionParams.MODE_FULL_INSTALL);
65                     int sessionId = packageInstaller.createSession(params);
66                     session = packageInstaller.openSession(sessionId);
67 
68                     addApkToInstallSession("HelloActivity.apk", session);
69 
70                     // Create an install status receiver.
71                     Context context = InstallApkSessionApi.this;
72                     Intent intent = new Intent(context, InstallApkSessionApi.class);
73                     intent.setAction(PACKAGE_INSTALLED_ACTION);
74                     PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
75                     IntentSender statusReceiver = pendingIntent.getIntentSender();
76 
77                     // Commit the session (this will start the installation workflow).
78                     session.commit(statusReceiver);
79                 } catch (IOException e) {
80                     throw new RuntimeException("Couldn't install package", e);
81                 } catch (RuntimeException e) {
82                     if (session != null) {
83                         session.abandon();
84                     }
85                     throw e;
86                 }
87             }
88         });
89     }
90 
addApkToInstallSession(String assetName, PackageInstaller.Session session)91     private void addApkToInstallSession(String assetName, PackageInstaller.Session session)
92             throws IOException {
93         // It's recommended to pass the file size to openWrite(). Otherwise installation may fail
94         // if the disk is almost full.
95         try (OutputStream packageInSession = session.openWrite("package", 0, -1);
96              InputStream is = getAssets().open(assetName)) {
97             byte[] buffer = new byte[16384];
98             int n;
99             while ((n = is.read(buffer)) >= 0) {
100                 packageInSession.write(buffer, 0, n);
101             }
102         }
103     }
104 
105     // Note: this Activity must run in singleTop launchMode for it to be able to receive the intent
106     // in onNewIntent().
107     @Override
onNewIntent(Intent intent)108     protected void onNewIntent(Intent intent) {
109         Bundle extras = intent.getExtras();
110         if (PACKAGE_INSTALLED_ACTION.equals(intent.getAction())) {
111             int status = extras.getInt(PackageInstaller.EXTRA_STATUS);
112             String message = extras.getString(PackageInstaller.EXTRA_STATUS_MESSAGE);
113 
114             switch (status) {
115                 case PackageInstaller.STATUS_PENDING_USER_ACTION:
116                     // This test app isn't privileged, so the user has to confirm the install.
117                     Intent confirmIntent = (Intent) extras.get(Intent.EXTRA_INTENT);
118                     startActivity(confirmIntent);
119                     break;
120 
121                 case PackageInstaller.STATUS_SUCCESS:
122                     Toast.makeText(this, "Install succeeded!", Toast.LENGTH_SHORT).show();
123                     break;
124 
125                 case PackageInstaller.STATUS_FAILURE:
126                 case PackageInstaller.STATUS_FAILURE_ABORTED:
127                 case PackageInstaller.STATUS_FAILURE_BLOCKED:
128                 case PackageInstaller.STATUS_FAILURE_CONFLICT:
129                 case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE:
130                 case PackageInstaller.STATUS_FAILURE_INVALID:
131                 case PackageInstaller.STATUS_FAILURE_STORAGE:
132                     Toast.makeText(this, "Install failed! " + status + ", " + message,
133                             Toast.LENGTH_SHORT).show();
134                     break;
135                 default:
136                     Toast.makeText(this, "Unrecognized status received from installer: " + status,
137                             Toast.LENGTH_SHORT).show();
138             }
139         }
140     }
141 }
142