1 /*
2  * Copyright (C) 2016 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.android.packageinstaller;
18 
19 import android.annotation.Nullable;
20 import android.app.Activity;
21 import android.app.AlertDialog;
22 import android.app.Dialog;
23 import android.app.DialogFragment;
24 import android.content.Context;
25 import android.content.DialogInterface;
26 import android.content.Intent;
27 import android.content.pm.ApplicationInfo;
28 import android.content.pm.PackageInstaller;
29 import android.content.pm.PackageManager;
30 import android.net.Uri;
31 import android.os.Bundle;
32 import android.util.Log;
33 import android.view.View;
34 
35 import com.android.internal.app.AlertActivity;
36 
37 import java.io.File;
38 
39 /**
40  * Installation failed: Return status code to the caller or display failure UI to user
41  */
42 public class InstallFailed extends AlertActivity {
43     private static final String LOG_TAG = InstallFailed.class.getSimpleName();
44 
45     /** Label of the app that failed to install */
46     private CharSequence mLabel;
47 
48     /**
49      * Unhide the appropriate label for the statusCode.
50      *
51      * @param statusCode The status code from the package installer.
52      */
setExplanationFromErrorCode(int statusCode)53     private void setExplanationFromErrorCode(int statusCode) {
54         Log.d(LOG_TAG, "Installation status code: " + statusCode);
55 
56         View viewToEnable;
57         switch (statusCode) {
58             case PackageInstaller.STATUS_FAILURE_BLOCKED:
59                 viewToEnable = requireViewById(R.id.install_failed_blocked);
60                 break;
61             case PackageInstaller.STATUS_FAILURE_CONFLICT:
62                 viewToEnable = requireViewById(R.id.install_failed_conflict);
63                 break;
64             case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE:
65                 viewToEnable = requireViewById(R.id.install_failed_incompatible);
66                 break;
67             case PackageInstaller.STATUS_FAILURE_INVALID:
68                 viewToEnable = requireViewById(R.id.install_failed_invalid_apk);
69                 break;
70             default:
71                 viewToEnable = requireViewById(R.id.install_failed);
72                 break;
73         }
74 
75         viewToEnable.setVisibility(View.VISIBLE);
76     }
77 
78     @Override
onCreate(@ullable Bundle savedInstanceState)79     protected void onCreate(@Nullable Bundle savedInstanceState) {
80         super.onCreate(savedInstanceState);
81 
82         int statusCode = getIntent().getIntExtra(PackageInstaller.EXTRA_STATUS,
83                 PackageInstaller.STATUS_FAILURE);
84 
85         if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
86             int legacyStatus = getIntent().getIntExtra(PackageInstaller.EXTRA_LEGACY_STATUS,
87                     PackageManager.INSTALL_FAILED_INTERNAL_ERROR);
88 
89             // Return result if requested
90             Intent result = new Intent();
91             result.putExtra(Intent.EXTRA_INSTALL_RESULT, legacyStatus);
92             setResult(Activity.RESULT_FIRST_USER, result);
93             finish();
94         } else {
95             Intent intent = getIntent();
96             ApplicationInfo appInfo = intent
97                     .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
98             Uri packageURI = intent.getData();
99 
100             // Set header icon and title
101             PackageUtil.AppSnippet as;
102             PackageManager pm = getPackageManager();
103 
104             if ("package".equals(packageURI.getScheme())) {
105                 as = new PackageUtil.AppSnippet(pm.getApplicationLabel(appInfo),
106                         pm.getApplicationIcon(appInfo));
107             } else {
108                 final File sourceFile = new File(packageURI.getPath());
109                 as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
110             }
111 
112             // Store label for dialog
113             mLabel = as.label;
114 
115             mAlert.setIcon(as.icon);
116             mAlert.setTitle(as.label);
117             mAlert.setView(R.layout.install_content_view);
118             mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.done),
119                     (ignored, ignored2) -> finish(), null);
120             setupAlert();
121 
122             // Show out of space dialog if needed
123             if (statusCode == PackageInstaller.STATUS_FAILURE_STORAGE) {
124                 (new OutOfSpaceDialog()).show(getFragmentManager(), "outofspace");
125             }
126 
127             // Get status messages
128             setExplanationFromErrorCode(statusCode);
129         }
130     }
131 
132     /**
133      * Dialog shown when we ran out of space during installation. This contains a link to the
134      * "manage applications" settings page.
135      */
136     public static class OutOfSpaceDialog extends DialogFragment {
137         private InstallFailed mActivity;
138 
139         @Override
onAttach(Context context)140         public void onAttach(Context context) {
141             super.onAttach(context);
142 
143             mActivity = (InstallFailed) context;
144         }
145 
146         @Override
onCreateDialog(Bundle savedInstanceState)147         public Dialog onCreateDialog(Bundle savedInstanceState) {
148             return new AlertDialog.Builder(mActivity)
149                     .setTitle(R.string.out_of_space_dlg_title)
150                     .setMessage(getString(R.string.out_of_space_dlg_text, mActivity.mLabel))
151                     .setPositiveButton(R.string.manage_applications, (dialog, which) -> {
152                         // launch manage applications
153                         Intent intent = new Intent("android.intent.action.MANAGE_PACKAGE_STORAGE");
154                         startActivity(intent);
155                         mActivity.finish();
156                     })
157                     .setNegativeButton(R.string.cancel, (dialog, which) -> mActivity.finish())
158                     .create();
159         }
160 
161         @Override
onCancel(DialogInterface dialog)162         public void onCancel(DialogInterface dialog) {
163             super.onCancel(dialog);
164 
165             mActivity.finish();
166         }
167     }
168 }
169