InstalledAppDetails.java revision 44178e2801c013e60defb4b5f390d893e7344a71
1/**
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy
6 * 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, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
14 * under the License.
15 */
16
17package com.android.settings.applications;
18
19import com.android.internal.content.PackageHelper;
20import com.android.settings.R;
21
22import android.app.Activity;
23import android.app.ActivityManager;
24import android.app.AlertDialog;
25import android.app.Dialog;
26import android.content.BroadcastReceiver;
27import android.content.Context;
28import android.content.DialogInterface;
29import android.content.Intent;
30import android.content.IntentFilter;
31import android.content.pm.ApplicationInfo;
32import android.content.pm.IPackageDataObserver;
33import android.content.pm.IPackageManager;
34import android.content.pm.IPackageMoveObserver;
35import android.content.pm.PackageInfo;
36import android.content.pm.PackageManager;
37import android.content.pm.ResolveInfo;
38import android.content.pm.PackageManager.NameNotFoundException;
39import android.net.Uri;
40import android.os.AsyncTask;
41import android.os.Bundle;
42import android.os.Handler;
43import android.os.Message;
44import android.os.RemoteException;
45import android.os.ServiceManager;
46import android.text.format.Formatter;
47import android.util.Log;
48
49import java.lang.ref.WeakReference;
50import java.util.ArrayList;
51import java.util.List;
52import android.content.ComponentName;
53import android.view.View;
54import android.widget.AppSecurityPermissions;
55import android.widget.Button;
56import android.widget.ImageView;
57import android.widget.LinearLayout;
58import android.widget.TextView;
59
60/**
61 * Activity to display application information from Settings. This activity presents
62 * extended information associated with a package like code, data, total size, permissions
63 * used by the application and also the set of default launchable activities.
64 * For system applications, an option to clear user data is displayed only if data size is > 0.
65 * System applications that do not want clear user data do not have this option.
66 * For non-system applications, there is no option to clear data. Instead there is an option to
67 * uninstall the application.
68 */
69public class InstalledAppDetails extends Activity
70        implements View.OnClickListener, ApplicationsState.Callbacks {
71    private static final String TAG="InstalledAppDetails";
72    static final boolean SUPPORT_DISABLE_APPS = false;
73
74    private PackageManager mPm;
75    private ApplicationsState mState;
76    private ApplicationsState.AppEntry mAppEntry;
77    private PackageInfo mPackageInfo;
78    private Button mUninstallButton;
79    private boolean mMoveInProgress = false;
80    private boolean mUpdatedSysApp = false;
81    private Button mActivitiesButton;
82    private boolean localLOGV = false;
83    private TextView mAppVersion;
84    private TextView mTotalSize;
85    private TextView mAppSize;
86    private TextView mDataSize;
87    private ClearUserDataObserver mClearDataObserver;
88    // Views related to cache info
89    private TextView mCacheSize;
90    private Button mClearCacheButton;
91    private ClearCacheObserver mClearCacheObserver;
92    private Button mForceStopButton;
93    private Button mClearDataButton;
94    private Button mMoveAppButton;
95    private int mMoveErrorCode;
96
97    private PackageMoveObserver mPackageMoveObserver;
98
99    private boolean mHaveSizes = false;
100    private long mLastCodeSize = -1;
101    private long mLastDataSize = -1;
102    private long mLastCacheSize = -1;
103    private long mLastTotalSize = -1;
104
105    //internal constants used in Handler
106    private static final int OP_SUCCESSFUL = 1;
107    private static final int OP_FAILED = 2;
108    private static final int CLEAR_USER_DATA = 1;
109    private static final int CLEAR_CACHE = 3;
110    private static final int PACKAGE_MOVE = 4;
111
112    // invalid size value used initially and also when size retrieval through PackageManager
113    // fails for whatever reason
114    private static final int SIZE_INVALID = -1;
115
116    // Resource strings
117    private CharSequence mInvalidSizeStr;
118    private CharSequence mComputingStr;
119
120    // Dialog identifiers used in showDialog
121    private static final int DLG_BASE = 0;
122    private static final int DLG_CLEAR_DATA = DLG_BASE + 1;
123    private static final int DLG_FACTORY_RESET = DLG_BASE + 2;
124    private static final int DLG_APP_NOT_FOUND = DLG_BASE + 3;
125    private static final int DLG_CANNOT_CLEAR_DATA = DLG_BASE + 4;
126    private static final int DLG_FORCE_STOP = DLG_BASE + 5;
127    private static final int DLG_MOVE_FAILED = DLG_BASE + 6;
128
129    private Handler mHandler = new Handler() {
130        public void handleMessage(Message msg) {
131            // If the activity is gone, don't process any more messages.
132            if (isFinishing()) {
133                return;
134            }
135            switch (msg.what) {
136                case CLEAR_USER_DATA:
137                    processClearMsg(msg);
138                    break;
139                case CLEAR_CACHE:
140                    // Refresh size info
141                    mState.requestSize(mAppEntry.info.packageName);
142                    break;
143                case PACKAGE_MOVE:
144                    processMoveMsg(msg);
145                    break;
146                default:
147                    break;
148            }
149        }
150    };
151
152    class ClearUserDataObserver extends IPackageDataObserver.Stub {
153       public void onRemoveCompleted(final String packageName, final boolean succeeded) {
154           final Message msg = mHandler.obtainMessage(CLEAR_USER_DATA);
155           msg.arg1 = succeeded?OP_SUCCESSFUL:OP_FAILED;
156           mHandler.sendMessage(msg);
157        }
158    }
159
160    class ClearCacheObserver extends IPackageDataObserver.Stub {
161        public void onRemoveCompleted(final String packageName, final boolean succeeded) {
162            final Message msg = mHandler.obtainMessage(CLEAR_CACHE);
163            msg.arg1 = succeeded ? OP_SUCCESSFUL:OP_FAILED;
164            mHandler.sendMessage(msg);
165         }
166     }
167
168    class PackageMoveObserver extends IPackageMoveObserver.Stub {
169        public void packageMoved(String packageName, int returnCode) throws RemoteException {
170            final Message msg = mHandler.obtainMessage(PACKAGE_MOVE);
171            msg.arg1 = returnCode;
172            mHandler.sendMessage(msg);
173        }
174    }
175
176    private String getSizeStr(long size) {
177        if (size == SIZE_INVALID) {
178            return mInvalidSizeStr.toString();
179        }
180        return Formatter.formatFileSize(this, size);
181    }
182
183    private void initDataButtons() {
184        if (mAppEntry.info.manageSpaceActivityName != null) {
185            mClearDataButton.setText(R.string.manage_space_text);
186        } else {
187            mClearDataButton.setText(R.string.clear_user_data_text);
188        }
189        mClearDataButton.setOnClickListener(this);
190    }
191
192    private CharSequence getMoveErrMsg(int errCode) {
193        switch (errCode) {
194            case PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE:
195                return getString(R.string.insufficient_storage);
196            case PackageManager.MOVE_FAILED_DOESNT_EXIST:
197                return getString(R.string.does_not_exist);
198            case PackageManager.MOVE_FAILED_FORWARD_LOCKED:
199                return getString(R.string.app_forward_locked);
200            case PackageManager.MOVE_FAILED_INVALID_LOCATION:
201                return getString(R.string.invalid_location);
202            case PackageManager.MOVE_FAILED_SYSTEM_PACKAGE:
203                return getString(R.string.system_package);
204            case PackageManager.MOVE_FAILED_INTERNAL_ERROR:
205                return "";
206        }
207        return "";
208    }
209
210    private void initMoveButton() {
211        boolean dataOnly = false;
212        dataOnly = (mPackageInfo == null) && (mAppEntry != null);
213        boolean moveDisable = true;
214        if (dataOnly) {
215            mMoveAppButton.setText(R.string.move_app);
216        } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
217            mMoveAppButton.setText(R.string.move_app_to_internal);
218            // Always let apps move to internal storage from sdcard.
219            moveDisable = false;
220        } else {
221            mMoveAppButton.setText(R.string.move_app_to_sdcard);
222            if ((mAppEntry.info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0 &&
223                    (mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0 &&
224                    mPackageInfo != null) {
225                if (mPackageInfo.installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL ||
226                        mPackageInfo.installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
227                    moveDisable = false;
228                } else if (mPackageInfo.installLocation
229                        == PackageInfo.INSTALL_LOCATION_UNSPECIFIED) {
230                    IPackageManager ipm  = IPackageManager.Stub.asInterface(
231                            ServiceManager.getService("package"));
232                    int loc;
233                    try {
234                        loc = ipm.getInstallLocation();
235                    } catch (RemoteException e) {
236                        Log.e(TAG, "Is Pakage Manager running?");
237                        return;
238                    }
239                    if (loc == PackageHelper.APP_INSTALL_EXTERNAL) {
240                        // For apps with no preference and the default value set
241                        // to install on sdcard.
242                        moveDisable = false;
243                    }
244                }
245            }
246        }
247        if (moveDisable) {
248            mMoveAppButton.setEnabled(false);
249        } else {
250            mMoveAppButton.setOnClickListener(this);
251            mMoveAppButton.setEnabled(true);
252        }
253    }
254
255    private void initUninstallButtons() {
256        mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
257        boolean enabled = true;
258        if (mUpdatedSysApp) {
259            mUninstallButton.setText(R.string.app_factory_reset);
260        } else {
261            if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
262                enabled = false;
263                if (SUPPORT_DISABLE_APPS) {
264                    try {
265                        // Try to prevent the user from bricking their phone
266                        // by not allowing disabling of apps signed with the
267                        // system cert and any launcher app in the system.
268                        PackageInfo sys = mPm.getPackageInfo("android",
269                                PackageManager.GET_SIGNATURES);
270                        Intent intent = new Intent(Intent.ACTION_MAIN);
271                        intent.addCategory(Intent.CATEGORY_HOME);
272                        intent.setPackage(mAppEntry.info.packageName);
273                        List<ResolveInfo> homes = mPm.queryIntentActivities(intent, 0);
274                        if ((homes != null && homes.size() > 0) ||
275                                (mPackageInfo != null &&
276                                        sys.signatures[0].equals(mPackageInfo.signatures[0]))) {
277                            // Disable button for core system applications.
278                            mUninstallButton.setText(R.string.disable_text);
279                        } else if (mAppEntry.info.enabled) {
280                            mUninstallButton.setText(R.string.disable_text);
281                            enabled = true;
282                        } else {
283                            mUninstallButton.setText(R.string.enable_text);
284                            enabled = true;
285                        }
286                    } catch (PackageManager.NameNotFoundException e) {
287                        Log.w(TAG, "Unable to get package info", e);
288                    }
289                }
290            } else {
291                mUninstallButton.setText(R.string.uninstall_text);
292            }
293        }
294        mUninstallButton.setEnabled(enabled);
295        if (enabled) {
296            // Register listener
297            mUninstallButton.setOnClickListener(this);
298        }
299    }
300
301    /** Called when the activity is first created. */
302    @Override
303    protected void onCreate(Bundle icicle) {
304        super.onCreate(icicle);
305
306        mState = ApplicationsState.getInstance(getApplication());
307        mPm = getPackageManager();
308
309        setContentView(R.layout.installed_app_details);
310
311        mComputingStr = getText(R.string.computing_size);
312
313        // Set default values on sizes
314        mTotalSize = (TextView)findViewById(R.id.total_size_text);
315        mAppSize = (TextView)findViewById(R.id.application_size_text);
316        mDataSize = (TextView)findViewById(R.id.data_size_text);
317
318        // Get Control button panel
319        View btnPanel = findViewById(R.id.control_buttons_panel);
320        mForceStopButton = (Button) btnPanel.findViewById(R.id.left_button);
321        mForceStopButton.setText(R.string.force_stop);
322        mUninstallButton = (Button)btnPanel.findViewById(R.id.right_button);
323        mForceStopButton.setEnabled(false);
324
325        // Initialize clear data and move install location buttons
326        View data_buttons_panel = findViewById(R.id.data_buttons_panel);
327        mClearDataButton = (Button) data_buttons_panel.findViewById(R.id.left_button);
328        mMoveAppButton = (Button) data_buttons_panel.findViewById(R.id.right_button);
329
330        // Cache section
331        mCacheSize = (TextView) findViewById(R.id.cache_size_text);
332        mClearCacheButton = (Button) findViewById(R.id.clear_cache_button);
333
334        mActivitiesButton = (Button)findViewById(R.id.clear_activities_button);
335    }
336
337    // Utility method to set applicaiton label and icon.
338    private void setAppLabelAndIcon(PackageInfo pkgInfo) {
339        View appSnippet = findViewById(R.id.app_snippet);
340        ImageView icon = (ImageView) appSnippet.findViewById(R.id.app_icon);
341        mState.ensureIcon(mAppEntry);
342        icon.setImageDrawable(mAppEntry.icon);
343        // Set application name.
344        TextView label = (TextView) appSnippet.findViewById(R.id.app_name);
345        label.setText(mAppEntry.label);
346        // Version number of application
347        mAppVersion = (TextView) appSnippet.findViewById(R.id.app_size);
348
349        if (pkgInfo != null && pkgInfo.versionName != null) {
350            mAppVersion.setVisibility(View.VISIBLE);
351            mAppVersion.setText(getString(R.string.version_text,
352                    String.valueOf(pkgInfo.versionName)));
353        } else {
354            mAppVersion.setVisibility(View.INVISIBLE);
355        }
356    }
357
358    @Override
359    public void onResume() {
360        super.onResume();
361
362        mState.resume(this);
363        if (!refreshUi()) {
364            setIntentAndFinish(true, true);
365        }
366    }
367
368    @Override
369    public void onPause() {
370        super.onPause();
371        mState.pause();
372    }
373
374    @Override
375    public void onAllSizesComputed() {
376    }
377
378    @Override
379    public void onPackageIconChanged() {
380    }
381
382    @Override
383    public void onPackageListChanged() {
384        refreshUi();
385    }
386
387    @Override
388    public void onPackageSizeChanged(String packageName) {
389        if (packageName.equals(mAppEntry.info.packageName)) {
390            refreshSizeInfo();
391        }
392    }
393
394    @Override
395    public void onRunningStateChanged(boolean running) {
396    }
397
398    private boolean refreshUi() {
399        if (mMoveInProgress) {
400            return true;
401        }
402
403        Intent intent = getIntent();
404        final String packageName = intent.getData().getSchemeSpecificPart();
405        mAppEntry = mState.getEntry(packageName);
406
407        if (mAppEntry == null) {
408            return false; // onCreate must have failed, make sure to exit
409        }
410
411        // Get application info again to refresh changed properties of application
412        try {
413            mPackageInfo = mPm.getPackageInfo(mAppEntry.info.packageName,
414                    PackageManager.GET_DISABLED_COMPONENTS |
415                    PackageManager.GET_UNINSTALLED_PACKAGES |
416                    PackageManager.GET_SIGNATURES);
417        } catch (NameNotFoundException e) {
418            Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e);
419            return false; // onCreate must have failed, make sure to exit
420        }
421
422        // Get list of preferred activities
423        List<ComponentName> prefActList = new ArrayList<ComponentName>();
424
425        // Intent list cannot be null. so pass empty list
426        List<IntentFilter> intentList = new ArrayList<IntentFilter>();
427        mPm.getPreferredActivities(intentList, prefActList, packageName);
428        if(localLOGV) Log.i(TAG, "Have "+prefActList.size()+" number of activities in prefered list");
429        TextView autoLaunchView = (TextView)findViewById(R.id.auto_launch);
430        if (prefActList.size() <= 0) {
431            // Disable clear activities button
432            autoLaunchView.setText(R.string.auto_launch_disable_text);
433            mActivitiesButton.setEnabled(false);
434        } else {
435            autoLaunchView.setText(R.string.auto_launch_enable_text);
436            mActivitiesButton.setEnabled(true);
437            mActivitiesButton.setOnClickListener(this);
438        }
439
440        // Security permissions section
441        LinearLayout permsView = (LinearLayout) findViewById(R.id.permissions_section);
442        AppSecurityPermissions asp = new AppSecurityPermissions(this, packageName);
443        if (asp.getPermissionCount() > 0) {
444            permsView.setVisibility(View.VISIBLE);
445            // Make the security sections header visible
446            LinearLayout securityList = (LinearLayout) permsView.findViewById(
447                    R.id.security_settings_list);
448            securityList.removeAllViews();
449            securityList.addView(asp.getPermissionsView());
450        } else {
451            permsView.setVisibility(View.GONE);
452        }
453
454        checkForceStop();
455        setAppLabelAndIcon(mPackageInfo);
456        refreshButtons();
457        refreshSizeInfo();
458        return true;
459    }
460
461    private void setIntentAndFinish(boolean finish, boolean appChanged) {
462        if(localLOGV) Log.i(TAG, "appChanged="+appChanged);
463        Intent intent = new Intent();
464        intent.putExtra(ManageApplications.APP_CHG, appChanged);
465        setResult(ManageApplications.RESULT_OK, intent);
466        if(finish) {
467            finish();
468        }
469    }
470
471    private void refreshSizeInfo() {
472        if (mAppEntry.size == ApplicationsState.SIZE_INVALID
473                || mAppEntry.size == ApplicationsState.SIZE_UNKNOWN) {
474            mLastCodeSize = mLastDataSize = mLastCacheSize = mLastTotalSize = -1;
475            if (!mHaveSizes) {
476                mAppSize.setText(mComputingStr);
477                mDataSize.setText(mComputingStr);
478                mCacheSize.setText(mComputingStr);
479                mTotalSize.setText(mComputingStr);
480            }
481            mClearDataButton.setEnabled(false);
482            mClearCacheButton.setEnabled(false);
483
484        } else {
485            mHaveSizes = true;
486            if (mLastCodeSize != mAppEntry.codeSize) {
487                mLastCodeSize = mAppEntry.codeSize;
488                mAppSize.setText(getSizeStr(mAppEntry.codeSize));
489            }
490            if (mLastDataSize != mAppEntry.dataSize) {
491                mLastDataSize = mAppEntry.dataSize;
492                mDataSize.setText(getSizeStr(mAppEntry.dataSize));
493            }
494            if (mLastCacheSize != mAppEntry.cacheSize) {
495                mLastCacheSize = mAppEntry.cacheSize;
496                mCacheSize.setText(getSizeStr(mAppEntry.cacheSize));
497            }
498            if (mLastTotalSize != mAppEntry.size) {
499                mLastTotalSize = mAppEntry.size;
500                mTotalSize.setText(getSizeStr(mAppEntry.size));
501            }
502
503            if (mAppEntry.dataSize <= 0) {
504                mClearDataButton.setEnabled(false);
505            } else {
506                mClearDataButton.setEnabled(true);
507                mClearDataButton.setOnClickListener(this);
508            }
509            if (mAppEntry.cacheSize <= 0) {
510                mClearCacheButton.setEnabled(false);
511            } else {
512                mClearCacheButton.setEnabled(true);
513                mClearCacheButton.setOnClickListener(this);
514            }
515        }
516    }
517
518    /*
519     * Private method to handle clear message notification from observer when
520     * the async operation from PackageManager is complete
521     */
522    private void processClearMsg(Message msg) {
523        int result = msg.arg1;
524        String packageName = mAppEntry.info.packageName;
525        mClearDataButton.setText(R.string.clear_user_data_text);
526        if(result == OP_SUCCESSFUL) {
527            Log.i(TAG, "Cleared user data for package : "+packageName);
528            mState.requestSize(mAppEntry.info.packageName);
529        } else {
530            mClearDataButton.setEnabled(true);
531        }
532        checkForceStop();
533    }
534
535    private void refreshButtons() {
536        if (!mMoveInProgress) {
537            initUninstallButtons();
538            initDataButtons();
539            initMoveButton();
540        } else {
541            mMoveAppButton.setText(R.string.moving);
542            mMoveAppButton.setEnabled(false);
543            mUninstallButton.setEnabled(false);
544        }
545    }
546
547    private void processMoveMsg(Message msg) {
548        int result = msg.arg1;
549        String packageName = mAppEntry.info.packageName;
550        // Refresh the button attributes.
551        mMoveInProgress = false;
552        if (result == PackageManager.MOVE_SUCCEEDED) {
553            Log.i(TAG, "Moved resources for " + packageName);
554            // Refresh size information again.
555            mState.requestSize(mAppEntry.info.packageName);
556        } else {
557            mMoveErrorCode = result;
558            showDialogInner(DLG_MOVE_FAILED);
559        }
560        refreshUi();
561    }
562
563    /*
564     * Private method to initiate clearing user data when the user clicks the clear data
565     * button for a system package
566     */
567    private  void initiateClearUserData() {
568        mClearDataButton.setEnabled(false);
569        // Invoke uninstall or clear user data based on sysPackage
570        String packageName = mAppEntry.info.packageName;
571        Log.i(TAG, "Clearing user data for package : " + packageName);
572        if (mClearDataObserver == null) {
573            mClearDataObserver = new ClearUserDataObserver();
574        }
575        ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
576        boolean res = am.clearApplicationUserData(packageName, mClearDataObserver);
577        if (!res) {
578            // Clearing data failed for some obscure reason. Just log error for now
579            Log.i(TAG, "Couldnt clear application user data for package:"+packageName);
580            showDialogInner(DLG_CANNOT_CLEAR_DATA);
581        } else {
582            mClearDataButton.setText(R.string.recompute_size);
583        }
584    }
585
586    private void showDialogInner(int id) {
587        //removeDialog(id);
588        showDialog(id);
589    }
590
591    @Override
592    public Dialog onCreateDialog(int id, Bundle args) {
593        switch (id) {
594        case DLG_CLEAR_DATA:
595            return new AlertDialog.Builder(this)
596            .setTitle(getString(R.string.clear_data_dlg_title))
597            .setIcon(android.R.drawable.ic_dialog_alert)
598            .setMessage(getString(R.string.clear_data_dlg_text))
599            .setPositiveButton(R.string.dlg_ok,
600                    new DialogInterface.OnClickListener() {
601                public void onClick(DialogInterface dialog, int which) {
602                    // Clear user data here
603                    initiateClearUserData();
604                }
605            })
606            .setNegativeButton(R.string.dlg_cancel, null)
607            .create();
608        case DLG_FACTORY_RESET:
609            return new AlertDialog.Builder(this)
610            .setTitle(getString(R.string.app_factory_reset_dlg_title))
611            .setIcon(android.R.drawable.ic_dialog_alert)
612            .setMessage(getString(R.string.app_factory_reset_dlg_text))
613            .setPositiveButton(R.string.dlg_ok,
614                    new DialogInterface.OnClickListener() {
615                public void onClick(DialogInterface dialog, int which) {
616                    // Clear user data here
617                    uninstallPkg(mAppEntry.info.packageName);
618                }
619            })
620            .setNegativeButton(R.string.dlg_cancel, null)
621            .create();
622        case DLG_APP_NOT_FOUND:
623            return new AlertDialog.Builder(this)
624            .setTitle(getString(R.string.app_not_found_dlg_title))
625            .setIcon(android.R.drawable.ic_dialog_alert)
626            .setMessage(getString(R.string.app_not_found_dlg_title))
627            .setNeutralButton(getString(R.string.dlg_ok),
628                    new DialogInterface.OnClickListener() {
629                public void onClick(DialogInterface dialog, int which) {
630                    //force to recompute changed value
631                    setIntentAndFinish(true, true);
632                }
633            })
634            .create();
635        case DLG_CANNOT_CLEAR_DATA:
636            return new AlertDialog.Builder(this)
637            .setTitle(getString(R.string.clear_failed_dlg_title))
638            .setIcon(android.R.drawable.ic_dialog_alert)
639            .setMessage(getString(R.string.clear_failed_dlg_text))
640            .setNeutralButton(R.string.dlg_ok,
641                    new DialogInterface.OnClickListener() {
642                public void onClick(DialogInterface dialog, int which) {
643                    mClearDataButton.setEnabled(false);
644                    //force to recompute changed value
645                    setIntentAndFinish(false, false);
646                }
647            })
648            .create();
649        case DLG_FORCE_STOP:
650            return new AlertDialog.Builder(this)
651            .setTitle(getString(R.string.force_stop_dlg_title))
652            .setIcon(android.R.drawable.ic_dialog_alert)
653            .setMessage(getString(R.string.force_stop_dlg_text))
654            .setPositiveButton(R.string.dlg_ok,
655                new DialogInterface.OnClickListener() {
656                public void onClick(DialogInterface dialog, int which) {
657                    // Force stop
658                    forceStopPackage(mAppEntry.info.packageName);
659                }
660            })
661            .setNegativeButton(R.string.dlg_cancel, null)
662            .create();
663        case DLG_MOVE_FAILED:
664            CharSequence msg = getString(R.string.move_app_failed_dlg_text,
665                    getMoveErrMsg(mMoveErrorCode));
666            return new AlertDialog.Builder(this)
667            .setTitle(getString(R.string.move_app_failed_dlg_title))
668            .setIcon(android.R.drawable.ic_dialog_alert)
669            .setMessage(msg)
670            .setNeutralButton(R.string.dlg_ok, null)
671            .create();
672        }
673        return null;
674    }
675
676    private void uninstallPkg(String packageName) {
677         // Create new intent to launch Uninstaller activity
678        Uri packageURI = Uri.parse("package:"+packageName);
679        Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI);
680        startActivity(uninstallIntent);
681        setIntentAndFinish(true, true);
682    }
683
684    private void forceStopPackage(String pkgName) {
685        ActivityManager am = (ActivityManager)getSystemService(
686                Context.ACTIVITY_SERVICE);
687        am.forceStopPackage(pkgName);
688        checkForceStop();
689    }
690
691    private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
692        @Override
693        public void onReceive(Context context, Intent intent) {
694            mForceStopButton.setEnabled(getResultCode() != RESULT_CANCELED);
695            mForceStopButton.setOnClickListener(InstalledAppDetails.this);
696        }
697    };
698
699    private void checkForceStop() {
700        Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
701                Uri.fromParts("package", mAppEntry.info.packageName, null));
702        intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mAppEntry.info.packageName });
703        intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid);
704        sendOrderedBroadcast(intent, null, mCheckKillProcessesReceiver, null,
705                Activity.RESULT_CANCELED, null, null);
706    }
707
708    static class DisableChanger extends AsyncTask<Object, Object, Object> {
709        final PackageManager mPm;
710        final WeakReference<InstalledAppDetails> mActivity;
711        final ApplicationInfo mInfo;
712        final int mState;
713
714        DisableChanger(InstalledAppDetails activity, ApplicationInfo info, int state) {
715            mPm = activity.mPm;
716            mActivity = new WeakReference<InstalledAppDetails>(activity);
717            mInfo = info;
718            mState = state;
719        }
720
721        @Override
722        protected Object doInBackground(Object... params) {
723            mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0);
724            return null;
725        }
726    }
727
728    /*
729     * Method implementing functionality of buttons clicked
730     * @see android.view.View.OnClickListener#onClick(android.view.View)
731     */
732    public void onClick(View v) {
733        String packageName = mAppEntry.info.packageName;
734        if(v == mUninstallButton) {
735            if (mUpdatedSysApp) {
736                showDialogInner(DLG_FACTORY_RESET);
737            } else {
738                if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
739                    new DisableChanger(this, mAppEntry.info, mAppEntry.info.enabled ?
740                            PackageManager.COMPONENT_ENABLED_STATE_DISABLED
741                            : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).execute((Object)null);
742                } else {
743                    uninstallPkg(packageName);
744                }
745            }
746        } else if(v == mActivitiesButton) {
747            mPm.clearPackagePreferredActivities(packageName);
748            mActivitiesButton.setEnabled(false);
749        } else if(v == mClearDataButton) {
750            if (mAppEntry.info.manageSpaceActivityName != null) {
751                Intent intent = new Intent(Intent.ACTION_DEFAULT);
752                intent.setClassName(mAppEntry.info.packageName,
753                        mAppEntry.info.manageSpaceActivityName);
754                startActivityForResult(intent, -1);
755            } else {
756                showDialogInner(DLG_CLEAR_DATA);
757            }
758        } else if (v == mClearCacheButton) {
759            // Lazy initialization of observer
760            if (mClearCacheObserver == null) {
761                mClearCacheObserver = new ClearCacheObserver();
762            }
763            mPm.deleteApplicationCacheFiles(packageName, mClearCacheObserver);
764        } else if (v == mForceStopButton) {
765            showDialogInner(DLG_FORCE_STOP);
766            //forceStopPackage(mAppInfo.packageName);
767        } else if (v == mMoveAppButton) {
768            if (mPackageMoveObserver == null) {
769                mPackageMoveObserver = new PackageMoveObserver();
770            }
771            int moveFlags = (mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0 ?
772                    PackageManager.MOVE_INTERNAL : PackageManager.MOVE_EXTERNAL_MEDIA;
773            mMoveInProgress = true;
774            refreshButtons();
775            mPm.movePackage(mAppEntry.info.packageName, mPackageMoveObserver, moveFlags);
776        }
777    }
778}
779
780