• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 **
3 ** Copyright 2007, The Android Open Source Project
4 **
5 ** Licensed under the Apache License, Version 2.0 (the "License");
6 ** you may not use this file except in compliance with the License.
7 ** You may obtain a copy of the License at
8 **
9 **     http://www.apache.org/licenses/LICENSE-2.0
10 **
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 */
17 package com.android.packageinstaller;
18 
19 import com.android.packageinstaller.R;
20 
21 import android.app.Activity;
22 import android.app.AlertDialog;
23 import android.app.Dialog;
24 import android.content.DialogInterface;
25 import android.content.Intent;
26 import android.content.DialogInterface.OnCancelListener;
27 import android.content.pm.ApplicationInfo;
28 import android.content.pm.PackageManager;
29 import android.content.pm.PackageParser;
30 import android.content.pm.PackageManager.NameNotFoundException;
31 import android.net.Uri;
32 import android.os.Bundle;
33 import android.provider.Settings;
34 import android.util.Log;
35 import android.view.View;
36 import android.view.Window;
37 import android.view.View.OnClickListener;
38 import android.widget.AppSecurityPermissions;
39 import android.widget.Button;
40 import android.widget.LinearLayout;
41 
42 /*
43  * This activity is launched when a new application is installed via side loading
44  * The package is first parsed and the user is notified of parse errors via a dialog.
45  * If the package is successfully parsed, the user is notified to turn on the install unknown
46  * applications setting. A memory check is made at this point and the user is notified of out
47  * of memory conditions if any. If the package is already existing on the device,
48  * a confirmation dialog (to replace the existing package) is presented to the user.
49  * Based on the user response the package is then installed by launching InstallAppConfirm
50  * sub activity. All state transitions are handled in this activity
51  */
52 public class PackageInstallerActivity extends Activity implements OnCancelListener, OnClickListener {
53     private static final String TAG = "PackageInstaller";
54     private Uri mPackageURI;
55     private boolean localLOGV = false;
56     PackageManager mPm;
57     private PackageParser.Package mPkgInfo;
58 
59     // ApplicationInfo object primarily used for already existing applications
60     private ApplicationInfo mAppInfo = null;
61 
62     // View for install progress
63     View mInstallConfirm;
64     // Buttons to indicate user acceptance
65     private Button mOk;
66     private Button mCancel;
67 
68     // Dialog identifiers used in showDialog
69     private static final int DLG_BASE = 0;
70     private static final int DLG_REPLACE_APP = DLG_BASE + 1;
71     private static final int DLG_UNKNOWN_APPS = DLG_BASE + 2;
72     private static final int DLG_PACKAGE_ERROR = DLG_BASE + 3;
73     private static final int DLG_OUT_OF_SPACE = DLG_BASE + 4;
74     private static final int DLG_INSTALL_ERROR = DLG_BASE + 5;
75 
startInstallConfirm()76     private void startInstallConfirm() {
77         LinearLayout permsSection = (LinearLayout) mInstallConfirm.findViewById(R.id.permissions_section);
78         LinearLayout securityList = (LinearLayout) permsSection.findViewById(
79                 R.id.security_settings_list);
80         boolean permVisible = false;
81         if(mPkgInfo != null) {
82             AppSecurityPermissions asp = new AppSecurityPermissions(this, mPkgInfo);
83             if(asp.getPermissionCount() > 0) {
84                 permVisible = true;
85                 securityList.addView(asp.getPermissionsView());
86             }
87         }
88         if(!permVisible){
89             permsSection.setVisibility(View.INVISIBLE);
90         }
91         mInstallConfirm.setVisibility(View.VISIBLE);
92         mOk = (Button)findViewById(R.id.ok_button);
93         mCancel = (Button)findViewById(R.id.cancel_button);
94         mOk.setOnClickListener(this);
95         mCancel.setOnClickListener(this);
96     }
97 
showDialogInner(int id)98     private void showDialogInner(int id) {
99         // TODO better fix for this? Remove dialog so that it gets created again
100         removeDialog(id);
101         showDialog(id);
102     }
103 
104     @Override
onCreateDialog(int id, Bundle bundle)105     public Dialog onCreateDialog(int id, Bundle bundle) {
106         switch (id) {
107         case DLG_REPLACE_APP:
108             int msgId = R.string.dlg_app_replacement_statement;
109             // Customized text for system apps
110             if ((mAppInfo != null) && (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
111                 msgId = R.string.dlg_sys_app_replacement_statement;
112             }
113             return new AlertDialog.Builder(this)
114                     .setTitle(R.string.dlg_app_replacement_title)
115                     .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
116                         public void onClick(DialogInterface dialog, int which) {
117                             startInstallConfirm();
118                         }})
119                     .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
120                         public void onClick(DialogInterface dialog, int which) {
121                             Log.i(TAG, "Canceling installation");
122                             finish();
123                         }})
124                     .setMessage(msgId)
125                     .setOnCancelListener(this)
126                     .create();
127         case DLG_UNKNOWN_APPS:
128             return new AlertDialog.Builder(this)
129                     .setTitle(R.string.unknown_apps_dlg_title)
130                     .setMessage(R.string.unknown_apps_dlg_text)
131                     .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
132                         public void onClick(DialogInterface dialog, int which) {
133                             Log.i(TAG, "Finishing off activity so that user can navigate to settings manually");
134                             finish();
135                         }})
136                     .setPositiveButton(R.string.settings, new DialogInterface.OnClickListener() {
137                         public void onClick(DialogInterface dialog, int which) {
138                             Log.i(TAG, "Launching settings");
139                             launchSettingsAppAndFinish();
140                         }
141                     })
142                     .setOnCancelListener(this)
143                     .create();
144         case DLG_PACKAGE_ERROR :
145             return new AlertDialog.Builder(this)
146                     .setTitle(R.string.Parse_error_dlg_title)
147                     .setMessage(R.string.Parse_error_dlg_text)
148                     .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
149                         public void onClick(DialogInterface dialog, int which) {
150                             finish();
151                         }
152                     })
153                     .setOnCancelListener(this)
154                     .create();
155         case DLG_OUT_OF_SPACE:
156             // Guaranteed not to be null. will default to package name if not set by app
157             CharSequence appTitle = mPm.getApplicationLabel(mPkgInfo.applicationInfo);
158             String dlgText = getString(R.string.out_of_space_dlg_text,
159                     appTitle.toString());
160             return new AlertDialog.Builder(this)
161                     .setTitle(R.string.out_of_space_dlg_title)
162                     .setMessage(dlgText)
163                     .setPositiveButton(R.string.manage_applications, new DialogInterface.OnClickListener() {
164                         public void onClick(DialogInterface dialog, int which) {
165                             //launch manage applications
166                             Intent intent = new Intent("android.intent.action.MANAGE_PACKAGE_STORAGE");
167                             startActivity(intent);
168                             finish();
169                         }
170                     })
171                     .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
172                         public void onClick(DialogInterface dialog, int which) {
173                             Log.i(TAG, "Canceling installation");
174                             finish();
175                         }
176                   })
177                   .setOnCancelListener(this)
178                   .create();
179         case DLG_INSTALL_ERROR :
180             // Guaranteed not to be null. will default to package name if not set by app
181             CharSequence appTitle1 = mPm.getApplicationLabel(mPkgInfo.applicationInfo);
182             String dlgText1 = getString(R.string.install_failed_msg,
183                     appTitle1.toString());
184             return new AlertDialog.Builder(this)
185                     .setTitle(R.string.install_failed)
186                     .setNeutralButton(R.string.ok, new DialogInterface.OnClickListener() {
187                         public void onClick(DialogInterface dialog, int which) {
188                             finish();
189                         }
190                     })
191                     .setMessage(dlgText1)
192                     .setOnCancelListener(this)
193                     .create();
194        }
195        return null;
196    }
197 
198     private void launchSettingsAppAndFinish() {
199         //Create an intent to launch SettingsTwo activity
200         Intent launchSettingsIntent = new Intent(Settings.ACTION_APPLICATION_SETTINGS);
201         startActivity(launchSettingsIntent);
202         finish();
203     }
204 
205     private boolean isInstallingUnknownAppsAllowed() {
206         return Settings.Secure.getInt(getContentResolver(),
207             Settings.Secure.INSTALL_NON_MARKET_APPS, 0) > 0;
208     }
209 
210     private void initiateInstall() {
211         String pkgName = mPkgInfo.packageName;
212         // Check if there is already a package on the device with this name
213         // but it has been renamed to something else.
214         String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });
215         if (oldName != null && oldName.length > 0 && oldName[0] != null) {
216             pkgName = oldName[0];
217             mPkgInfo.setPackageName(pkgName);
218         }
219         // Check if package is already installed. display confirmation dialog if replacing pkg
220         try {
221             mAppInfo = mPm.getApplicationInfo(pkgName,
222                     PackageManager.GET_UNINSTALLED_PACKAGES);
223         } catch (NameNotFoundException e) {
224             mAppInfo = null;
225         }
226         if (mAppInfo == null) {
227             startInstallConfirm();
228         } else {
229             if(localLOGV) Log.i(TAG, "Replacing existing package:"+
230                     mPkgInfo.applicationInfo.packageName);
231             showDialogInner(DLG_REPLACE_APP);
232         }
233     }
234 
235     @Override
236     protected void onCreate(Bundle icicle) {
237         super.onCreate(icicle);
238         //get intent information
239         final Intent intent = getIntent();
240         mPackageURI = intent.getData();
241         mPm = getPackageManager();
242         mPkgInfo = PackageUtil.getPackageInfo(mPackageURI);
243 
244         // Check for parse errors
245         if(mPkgInfo == null) {
246             Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
247             showDialogInner(DLG_PACKAGE_ERROR);
248             return;
249         }
250 
251         //set view
252         requestWindowFeature(Window.FEATURE_NO_TITLE);
253         setContentView(R.layout.install_start);
254         mInstallConfirm = findViewById(R.id.install_confirm_panel);
255         mInstallConfirm.setVisibility(View.INVISIBLE);
256         PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this,
257                 mPkgInfo.applicationInfo, mPackageURI);
258         PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
259        //check setting
260         if(!isInstallingUnknownAppsAllowed()) {
261             //ask user to enable setting first
262             showDialogInner(DLG_UNKNOWN_APPS);
263             return;
264         }
265         initiateInstall();
266     }
267 
268     // Generic handling when pressing back key
269     public void onCancel(DialogInterface dialog) {
270         finish();
271     }
272 
273     public void onClick(View v) {
274         if(v == mOk) {
275             // Start subactivity to actually install the application
276             Intent newIntent = new Intent();
277             newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
278                     mPkgInfo.applicationInfo);
279             newIntent.setData(mPackageURI);
280             newIntent.setClass(this, InstallAppProgress.class);
281             String installerPackageName = getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);
282             if (installerPackageName != null) {
283                 newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, installerPackageName);
284             }
285             if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
286             startActivity(newIntent);
287             finish();
288         } else if(v == mCancel) {
289             // Cancel and finish
290             finish();
291         }
292     }
293 }
294