InstalledAppDetails.java revision b26a61d6433e2e5456563e5d33aa4f419a8966fb
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.telephony.ISms;
20import com.android.internal.telephony.SmsUsageMonitor;
21import com.android.settings.R;
22import com.android.settings.Utils;
23import com.android.settings.applications.ApplicationsState.AppEntry;
24
25import android.app.Activity;
26import android.app.ActivityManager;
27import android.app.AlertDialog;
28import android.app.Dialog;
29import android.app.DialogFragment;
30import android.app.Fragment;
31import android.app.INotificationManager;
32import android.app.admin.DevicePolicyManager;
33import android.appwidget.AppWidgetManager;
34import android.content.BroadcastReceiver;
35import android.content.ComponentName;
36import android.content.Context;
37import android.content.DialogInterface;
38import android.content.Intent;
39import android.content.IntentFilter;
40import android.content.pm.ApplicationInfo;
41import android.content.pm.IPackageDataObserver;
42import android.content.pm.IPackageMoveObserver;
43import android.content.pm.PackageInfo;
44import android.content.pm.PackageManager;
45import android.content.pm.ResolveInfo;
46import android.content.pm.PackageManager.NameNotFoundException;
47import android.content.res.Resources;
48import android.hardware.usb.IUsbManager;
49import android.net.Uri;
50import android.os.AsyncTask;
51import android.os.Bundle;
52import android.os.Environment;
53import android.os.Handler;
54import android.os.IBinder;
55import android.os.Message;
56import android.os.RemoteException;
57import android.os.ServiceManager;
58import android.os.UserHandle;
59import android.os.UserManager;
60import android.preference.PreferenceActivity;
61import android.text.SpannableString;
62import android.text.TextUtils;
63import android.text.format.Formatter;
64import android.text.style.BulletSpan;
65import android.util.Log;
66
67import java.lang.ref.WeakReference;
68import java.util.ArrayList;
69import java.util.List;
70import android.view.LayoutInflater;
71import android.view.Menu;
72import android.view.MenuInflater;
73import android.view.MenuItem;
74import android.view.View;
75import android.view.ViewGroup;
76import android.widget.AdapterView;
77import android.widget.AppSecurityPermissions;
78import android.widget.ArrayAdapter;
79import android.widget.Button;
80import android.widget.CheckBox;
81import android.widget.CompoundButton;
82import android.widget.ImageView;
83import android.widget.LinearLayout;
84import android.widget.Spinner;
85import android.widget.TextView;
86
87/**
88 * Activity to display application information from Settings. This activity presents
89 * extended information associated with a package like code, data, total size, permissions
90 * used by the application and also the set of default launchable activities.
91 * For system applications, an option to clear user data is displayed only if data size is > 0.
92 * System applications that do not want clear user data do not have this option.
93 * For non-system applications, there is no option to clear data. Instead there is an option to
94 * uninstall the application.
95 */
96public class InstalledAppDetails extends Fragment
97        implements View.OnClickListener, CompoundButton.OnCheckedChangeListener,
98        ApplicationsState.Callbacks {
99    private static final String TAG="InstalledAppDetails";
100    private static final boolean localLOGV = false;
101
102    public static final String ARG_PACKAGE_NAME = "package";
103
104    private PackageManager mPm;
105    private UserManager mUserManager;
106    private IUsbManager mUsbManager;
107    private AppWidgetManager mAppWidgetManager;
108    private DevicePolicyManager mDpm;
109    private ISms mSmsManager;
110    private ApplicationsState mState;
111    private ApplicationsState.Session mSession;
112    private ApplicationsState.AppEntry mAppEntry;
113    private boolean mInitialized;
114    private boolean mShowUninstalled;
115    private PackageInfo mPackageInfo;
116    private CanBeOnSdCardChecker mCanBeOnSdCardChecker;
117    private View mRootView;
118    private Button mUninstallButton;
119    private View mMoreControlButtons;
120    private Button mSpecialDisableButton;
121    private boolean mMoveInProgress = false;
122    private boolean mUpdatedSysApp = false;
123    private Button mActivitiesButton;
124    private View mScreenCompatSection;
125    private CheckBox mAskCompatibilityCB;
126    private CheckBox mEnableCompatibilityCB;
127    private boolean mCanClearData = true;
128    private TextView mAppVersion;
129    private TextView mTotalSize;
130    private TextView mAppSize;
131    private TextView mDataSize;
132    private TextView mExternalCodeSize;
133    private TextView mExternalDataSize;
134    private ClearUserDataObserver mClearDataObserver;
135    // Views related to cache info
136    private TextView mCacheSize;
137    private Button mClearCacheButton;
138    private ClearCacheObserver mClearCacheObserver;
139    private Button mForceStopButton;
140    private Button mClearDataButton;
141    private Button mMoveAppButton;
142    private CompoundButton mNotificationSwitch;
143
144    private PackageMoveObserver mPackageMoveObserver;
145
146    private boolean mDisableAfterUninstall;
147
148    private boolean mHaveSizes = false;
149    private long mLastCodeSize = -1;
150    private long mLastDataSize = -1;
151    private long mLastExternalCodeSize = -1;
152    private long mLastExternalDataSize = -1;
153    private long mLastCacheSize = -1;
154    private long mLastTotalSize = -1;
155
156    //internal constants used in Handler
157    private static final int OP_SUCCESSFUL = 1;
158    private static final int OP_FAILED = 2;
159    private static final int CLEAR_USER_DATA = 1;
160    private static final int CLEAR_CACHE = 3;
161    private static final int PACKAGE_MOVE = 4;
162
163    // invalid size value used initially and also when size retrieval through PackageManager
164    // fails for whatever reason
165    private static final int SIZE_INVALID = -1;
166
167    // Resource strings
168    private CharSequence mInvalidSizeStr;
169    private CharSequence mComputingStr;
170
171    // Dialog identifiers used in showDialog
172    private static final int DLG_BASE = 0;
173    private static final int DLG_CLEAR_DATA = DLG_BASE + 1;
174    private static final int DLG_FACTORY_RESET = DLG_BASE + 2;
175    private static final int DLG_APP_NOT_FOUND = DLG_BASE + 3;
176    private static final int DLG_CANNOT_CLEAR_DATA = DLG_BASE + 4;
177    private static final int DLG_FORCE_STOP = DLG_BASE + 5;
178    private static final int DLG_MOVE_FAILED = DLG_BASE + 6;
179    private static final int DLG_DISABLE = DLG_BASE + 7;
180    private static final int DLG_DISABLE_NOTIFICATIONS = DLG_BASE + 8;
181    private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 9;
182
183    // Menu identifiers
184    public static final int UNINSTALL_ALL_USERS_MENU = 1;
185
186    // Result code identifiers
187    public static final int REQUEST_UNINSTALL = 1;
188    public static final int REQUEST_MANAGE_SPACE = 2;
189
190    private Handler mHandler = new Handler() {
191        public void handleMessage(Message msg) {
192            // If the fragment is gone, don't process any more messages.
193            if (getView() == null) {
194                return;
195            }
196            switch (msg.what) {
197                case CLEAR_USER_DATA:
198                    processClearMsg(msg);
199                    break;
200                case CLEAR_CACHE:
201                    // Refresh size info
202                    mState.requestSize(mAppEntry.info.packageName);
203                    break;
204                case PACKAGE_MOVE:
205                    processMoveMsg(msg);
206                    break;
207                default:
208                    break;
209            }
210        }
211    };
212
213    class ClearUserDataObserver extends IPackageDataObserver.Stub {
214       public void onRemoveCompleted(final String packageName, final boolean succeeded) {
215           final Message msg = mHandler.obtainMessage(CLEAR_USER_DATA);
216           msg.arg1 = succeeded?OP_SUCCESSFUL:OP_FAILED;
217           mHandler.sendMessage(msg);
218        }
219    }
220
221    class ClearCacheObserver extends IPackageDataObserver.Stub {
222        public void onRemoveCompleted(final String packageName, final boolean succeeded) {
223            final Message msg = mHandler.obtainMessage(CLEAR_CACHE);
224            msg.arg1 = succeeded ? OP_SUCCESSFUL:OP_FAILED;
225            mHandler.sendMessage(msg);
226         }
227     }
228
229    class PackageMoveObserver extends IPackageMoveObserver.Stub {
230        public void packageMoved(String packageName, int returnCode) throws RemoteException {
231            final Message msg = mHandler.obtainMessage(PACKAGE_MOVE);
232            msg.arg1 = returnCode;
233            mHandler.sendMessage(msg);
234        }
235    }
236
237    private String getSizeStr(long size) {
238        if (size == SIZE_INVALID) {
239            return mInvalidSizeStr.toString();
240        }
241        return Formatter.formatFileSize(getActivity(), size);
242    }
243
244    private void initDataButtons() {
245        if ((mAppEntry.info.flags&(ApplicationInfo.FLAG_SYSTEM
246                | ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA))
247                == ApplicationInfo.FLAG_SYSTEM
248                || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
249            mClearDataButton.setText(R.string.clear_user_data_text);
250            mClearDataButton.setEnabled(false);
251            mCanClearData = false;
252        } else {
253            if (mAppEntry.info.manageSpaceActivityName != null) {
254                mClearDataButton.setText(R.string.manage_space_text);
255            } else {
256                mClearDataButton.setText(R.string.clear_user_data_text);
257            }
258            mClearDataButton.setOnClickListener(this);
259        }
260    }
261
262    private CharSequence getMoveErrMsg(int errCode) {
263        switch (errCode) {
264            case PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE:
265                return getActivity().getString(R.string.insufficient_storage);
266            case PackageManager.MOVE_FAILED_DOESNT_EXIST:
267                return getActivity().getString(R.string.does_not_exist);
268            case PackageManager.MOVE_FAILED_FORWARD_LOCKED:
269                return getActivity().getString(R.string.app_forward_locked);
270            case PackageManager.MOVE_FAILED_INVALID_LOCATION:
271                return getActivity().getString(R.string.invalid_location);
272            case PackageManager.MOVE_FAILED_SYSTEM_PACKAGE:
273                return getActivity().getString(R.string.system_package);
274            case PackageManager.MOVE_FAILED_INTERNAL_ERROR:
275                return "";
276        }
277        return "";
278    }
279
280    private void initMoveButton() {
281        if (Environment.isExternalStorageEmulated()) {
282            mMoveAppButton.setVisibility(View.INVISIBLE);
283            return;
284        }
285        boolean dataOnly = false;
286        dataOnly = (mPackageInfo == null) && (mAppEntry != null);
287        boolean moveDisable = true;
288        if (dataOnly) {
289            mMoveAppButton.setText(R.string.move_app);
290        } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
291            mMoveAppButton.setText(R.string.move_app_to_internal);
292            // Always let apps move to internal storage from sdcard.
293            moveDisable = false;
294        } else {
295            mMoveAppButton.setText(R.string.move_app_to_sdcard);
296            mCanBeOnSdCardChecker.init();
297            moveDisable = !mCanBeOnSdCardChecker.check(mAppEntry.info);
298        }
299        if (moveDisable) {
300            mMoveAppButton.setEnabled(false);
301        } else {
302            mMoveAppButton.setOnClickListener(this);
303            mMoveAppButton.setEnabled(true);
304        }
305    }
306
307    private boolean isThisASystemPackage() {
308        try {
309            PackageInfo sys = mPm.getPackageInfo("android", PackageManager.GET_SIGNATURES);
310            return (mPackageInfo != null && mPackageInfo.signatures != null &&
311                    sys.signatures[0].equals(mPackageInfo.signatures[0]));
312        } catch (PackageManager.NameNotFoundException e) {
313            return false;
314        }
315    }
316
317    private boolean handleDisableable(Button button) {
318        boolean disableable = false;
319        try {
320            // Try to prevent the user from bricking their phone
321            // by not allowing disabling of apps signed with the
322            // system cert and any launcher app in the system.
323            PackageInfo sys = mPm.getPackageInfo("android",
324                    PackageManager.GET_SIGNATURES);
325            Intent intent = new Intent(Intent.ACTION_MAIN);
326            intent.addCategory(Intent.CATEGORY_HOME);
327            intent.setPackage(mAppEntry.info.packageName);
328            List<ResolveInfo> homes = mPm.queryIntentActivities(intent, 0);
329            if ((homes != null && homes.size() > 0) || isThisASystemPackage()) {
330                // Disable button for core system applications.
331                button.setText(R.string.disable_text);
332            } else if (mAppEntry.info.enabled) {
333                button.setText(R.string.disable_text);
334                disableable = true;
335            } else {
336                button.setText(R.string.enable_text);
337                disableable = true;
338            }
339        } catch (PackageManager.NameNotFoundException e) {
340            Log.w(TAG, "Unable to get package info", e);
341        }
342        return disableable;
343    }
344
345    private void initUninstallButtons() {
346        mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
347        boolean enabled = true;
348        if (mUpdatedSysApp) {
349            mUninstallButton.setText(R.string.app_factory_reset);
350            boolean specialDisable = false;
351            if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
352                specialDisable = handleDisableable(mSpecialDisableButton);
353                mSpecialDisableButton.setOnClickListener(this);
354            }
355            mMoreControlButtons.setVisibility(specialDisable ? View.VISIBLE : View.GONE);
356        } else {
357            mMoreControlButtons.setVisibility(View.GONE);
358            if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
359                enabled = handleDisableable(mUninstallButton);
360            } else if ((mPackageInfo.applicationInfo.flags
361                    & ApplicationInfo.FLAG_INSTALLED) == 0
362                    && mUserManager.getUsers().size() >= 2) {
363                // When we have multiple users, there is a separate menu
364                // to uninstall for all users.
365                mUninstallButton.setText(R.string.uninstall_text);
366                enabled = false;
367            } else {
368                mUninstallButton.setText(R.string.uninstall_text);
369            }
370        }
371        // If this is a device admin, it can't be uninstall or disabled.
372        // We do this here so the text of the button is still set correctly.
373        if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
374            enabled = false;
375        }
376        mUninstallButton.setEnabled(enabled);
377        if (enabled) {
378            // Register listener
379            mUninstallButton.setOnClickListener(this);
380        }
381    }
382
383    private void initNotificationButton() {
384        INotificationManager nm = INotificationManager.Stub.asInterface(
385                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
386        boolean enabled = true; // default on
387        try {
388            enabled = nm.areNotificationsEnabledForPackage(mAppEntry.info.packageName,
389                    mAppEntry.info.uid);
390        } catch (android.os.RemoteException ex) {
391            // this does not bode well
392        }
393        mNotificationSwitch.setChecked(enabled);
394        if (isThisASystemPackage()) {
395            mNotificationSwitch.setEnabled(false);
396        } else {
397            mNotificationSwitch.setEnabled(true);
398            mNotificationSwitch.setOnCheckedChangeListener(this);
399        }
400    }
401
402    /** Called when the activity is first created. */
403    @Override
404    public void onCreate(Bundle icicle) {
405        super.onCreate(icicle);
406
407        mState = ApplicationsState.getInstance(getActivity().getApplication());
408        mSession = mState.newSession(this);
409        mPm = getActivity().getPackageManager();
410        mUserManager = (UserManager)getActivity().getSystemService(Context.USER_SERVICE);
411        IBinder b = ServiceManager.getService(Context.USB_SERVICE);
412        mUsbManager = IUsbManager.Stub.asInterface(b);
413        mAppWidgetManager = AppWidgetManager.getInstance(getActivity());
414        mDpm = (DevicePolicyManager)getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
415        mSmsManager = ISms.Stub.asInterface(ServiceManager.getService("isms"));
416
417        mCanBeOnSdCardChecker = new CanBeOnSdCardChecker();
418
419        // Need to make sure we have loaded applications at this point.
420        mSession.resume();
421
422        retrieveAppEntry();
423
424        setHasOptionsMenu(true);
425    }
426
427    @Override
428    public View onCreateView(
429            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
430        final View view = inflater.inflate(R.layout.installed_app_details, container, false);
431        Utils.prepareCustomPreferencesList(container, view, view, false);
432
433        mRootView = view;
434        mComputingStr = getActivity().getText(R.string.computing_size);
435
436        // Set default values on sizes
437        mTotalSize = (TextView)view.findViewById(R.id.total_size_text);
438        mAppSize = (TextView)view.findViewById(R.id.application_size_text);
439        mDataSize = (TextView)view.findViewById(R.id.data_size_text);
440        mExternalCodeSize = (TextView)view.findViewById(R.id.external_code_size_text);
441        mExternalDataSize = (TextView)view.findViewById(R.id.external_data_size_text);
442
443        if (Environment.isExternalStorageEmulated()) {
444            ((View)mExternalCodeSize.getParent()).setVisibility(View.GONE);
445            ((View)mExternalDataSize.getParent()).setVisibility(View.GONE);
446        }
447
448        // Get Control button panel
449        View btnPanel = view.findViewById(R.id.control_buttons_panel);
450        mForceStopButton = (Button) btnPanel.findViewById(R.id.left_button);
451        mForceStopButton.setText(R.string.force_stop);
452        mUninstallButton = (Button)btnPanel.findViewById(R.id.right_button);
453        mForceStopButton.setEnabled(false);
454
455        // Get More Control button panel
456        mMoreControlButtons = view.findViewById(R.id.more_control_buttons_panel);
457        mMoreControlButtons.findViewById(R.id.left_button).setVisibility(View.INVISIBLE);
458        mSpecialDisableButton = (Button)mMoreControlButtons.findViewById(R.id.right_button);
459        mMoreControlButtons.setVisibility(View.GONE);
460
461        // Initialize clear data and move install location buttons
462        View data_buttons_panel = view.findViewById(R.id.data_buttons_panel);
463        mClearDataButton = (Button) data_buttons_panel.findViewById(R.id.right_button);
464        mMoveAppButton = (Button) data_buttons_panel.findViewById(R.id.left_button);
465
466        // Cache section
467        mCacheSize = (TextView) view.findViewById(R.id.cache_size_text);
468        mClearCacheButton = (Button) view.findViewById(R.id.clear_cache_button);
469
470        mActivitiesButton = (Button)view.findViewById(R.id.clear_activities_button);
471
472        // Screen compatibility control
473        mScreenCompatSection = view.findViewById(R.id.screen_compatibility_section);
474        mAskCompatibilityCB = (CheckBox)view.findViewById(R.id.ask_compatibility_cb);
475        mEnableCompatibilityCB = (CheckBox)view.findViewById(R.id.enable_compatibility_cb);
476
477        mNotificationSwitch = (CompoundButton) view.findViewById(R.id.notification_switch);
478
479        return view;
480    }
481
482    @Override
483    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
484        menu.add(0, UNINSTALL_ALL_USERS_MENU, 1, R.string.uninstall_all_users_text)
485                .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
486    }
487
488    @Override
489    public void onPrepareOptionsMenu(Menu menu) {
490        boolean showIt = true;
491        if (mUpdatedSysApp) {
492            showIt = false;
493        } else if (mAppEntry == null) {
494            showIt = false;
495        } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
496            showIt = false;
497        } else if (mPackageInfo == null || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
498            showIt = false;
499        } else if (UserHandle.myUserId() != 0) {
500            showIt = false;
501        } else if (mUserManager.getUsers().size() < 2) {
502            showIt = false;
503        }
504        menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(showIt);
505    }
506
507    @Override
508    public boolean onOptionsItemSelected(MenuItem item) {
509        int menuId = item.getItemId();
510        if (menuId == UNINSTALL_ALL_USERS_MENU) {
511            uninstallPkg(mAppEntry.info.packageName, true, false);
512            return true;
513        }
514        return false;
515    }
516
517    @Override
518    public void onActivityResult(int requestCode, int resultCode, Intent data) {
519        super.onActivityResult(requestCode, resultCode, data);
520        if (requestCode == REQUEST_UNINSTALL) {
521            if (mDisableAfterUninstall) {
522                mDisableAfterUninstall = false;
523                try {
524                    ApplicationInfo ainfo = getActivity().getPackageManager().getApplicationInfo(
525                            mAppEntry.info.packageName, PackageManager.GET_UNINSTALLED_PACKAGES
526                            | PackageManager.GET_DISABLED_COMPONENTS);
527                    if ((ainfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) {
528                        new DisableChanger(this, mAppEntry.info,
529                                PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)
530                                .execute((Object)null);
531                    }
532                } catch (NameNotFoundException e) {
533                }
534            }
535            if (!refreshUi()) {
536                setIntentAndFinish(true, true);
537            }
538        }
539    }
540
541    // Utility method to set application label and icon.
542    private void setAppLabelAndIcon(PackageInfo pkgInfo) {
543        final View appSnippet = mRootView.findViewById(R.id.app_snippet);
544        appSnippet.setPaddingRelative(0, appSnippet.getPaddingTop(), 0, appSnippet.getPaddingBottom());
545
546        ImageView icon = (ImageView) appSnippet.findViewById(R.id.app_icon);
547        mState.ensureIcon(mAppEntry);
548        icon.setImageDrawable(mAppEntry.icon);
549        // Set application name.
550        TextView label = (TextView) appSnippet.findViewById(R.id.app_name);
551        label.setText(mAppEntry.label);
552        // Version number of application
553        mAppVersion = (TextView) appSnippet.findViewById(R.id.app_size);
554
555        if (pkgInfo != null && pkgInfo.versionName != null) {
556            mAppVersion.setVisibility(View.VISIBLE);
557            mAppVersion.setText(getActivity().getString(R.string.version_text,
558                    String.valueOf(pkgInfo.versionName)));
559        } else {
560            mAppVersion.setVisibility(View.INVISIBLE);
561        }
562    }
563
564    @Override
565    public void onResume() {
566        super.onResume();
567
568        mSession.resume();
569        if (!refreshUi()) {
570            setIntentAndFinish(true, true);
571        }
572    }
573
574    @Override
575    public void onPause() {
576        super.onPause();
577        mSession.pause();
578    }
579
580    @Override
581    public void onAllSizesComputed() {
582    }
583
584    @Override
585    public void onPackageIconChanged() {
586    }
587
588    @Override
589    public void onPackageListChanged() {
590        refreshUi();
591    }
592
593    @Override
594    public void onRebuildComplete(ArrayList<AppEntry> apps) {
595    }
596
597    @Override
598    public void onPackageSizeChanged(String packageName) {
599        if (packageName.equals(mAppEntry.info.packageName)) {
600            refreshSizeInfo();
601        }
602    }
603
604    @Override
605    public void onRunningStateChanged(boolean running) {
606    }
607
608    private String retrieveAppEntry() {
609        final Bundle args = getArguments();
610        String packageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null;
611        if (packageName == null) {
612            Intent intent = (args == null) ?
613                    getActivity().getIntent() : (Intent) args.getParcelable("intent");
614            if (intent != null) {
615                packageName = intent.getData().getSchemeSpecificPart();
616            }
617        }
618        mAppEntry = mState.getEntry(packageName);
619        if (mAppEntry != null) {
620            // Get application info again to refresh changed properties of application
621            try {
622                mPackageInfo = mPm.getPackageInfo(mAppEntry.info.packageName,
623                        PackageManager.GET_DISABLED_COMPONENTS |
624                        PackageManager.GET_UNINSTALLED_PACKAGES |
625                        PackageManager.GET_SIGNATURES);
626            } catch (NameNotFoundException e) {
627                Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e);
628            }
629        } else {
630            Log.w(TAG, "Missing AppEntry; maybe reinstalling?");
631            mPackageInfo = null;
632        }
633
634        return packageName;
635    }
636
637    private boolean refreshUi() {
638        if (mMoveInProgress) {
639            return true;
640        }
641        final String packageName = retrieveAppEntry();
642
643        if (mAppEntry == null) {
644            return false; // onCreate must have failed, make sure to exit
645        }
646
647        if (mPackageInfo == null) {
648            return false; // onCreate must have failed, make sure to exit
649        }
650
651        // Get list of preferred activities
652        List<ComponentName> prefActList = new ArrayList<ComponentName>();
653
654        // Intent list cannot be null. so pass empty list
655        List<IntentFilter> intentList = new ArrayList<IntentFilter>();
656        mPm.getPreferredActivities(intentList, prefActList, packageName);
657        if (localLOGV)
658            Log.i(TAG, "Have " + prefActList.size() + " number of activities in preferred list");
659        boolean hasUsbDefaults = false;
660        try {
661            hasUsbDefaults = mUsbManager.hasDefaults(packageName, UserHandle.myUserId());
662        } catch (RemoteException e) {
663            Log.e(TAG, "mUsbManager.hasDefaults", e);
664        }
665        boolean hasBindAppWidgetPermission =
666                mAppWidgetManager.hasBindAppWidgetPermission(mAppEntry.info.packageName);
667
668        TextView autoLaunchTitleView = (TextView) mRootView.findViewById(R.id.auto_launch_title);
669        TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch);
670        boolean autoLaunchEnabled = prefActList.size() > 0 || hasUsbDefaults;
671        if (!autoLaunchEnabled && !hasBindAppWidgetPermission) {
672            resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView);
673        } else {
674            boolean useBullets = hasBindAppWidgetPermission && autoLaunchEnabled;
675
676            if (hasBindAppWidgetPermission) {
677                autoLaunchTitleView.setText(R.string.auto_launch_label_generic);
678            } else {
679                autoLaunchTitleView.setText(R.string.auto_launch_label);
680            }
681
682            CharSequence text = null;
683            int bulletIndent = getResources()
684                    .getDimensionPixelSize(R.dimen.installed_app_details_bullet_offset);
685            if (autoLaunchEnabled) {
686                CharSequence autoLaunchEnableText = getText(R.string.auto_launch_enable_text);
687                SpannableString s = new SpannableString(autoLaunchEnableText);
688                if (useBullets) {
689                    s.setSpan(new BulletSpan(bulletIndent), 0, autoLaunchEnableText.length(), 0);
690                }
691                text = (text == null) ?
692                        TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n");
693            }
694            if (hasBindAppWidgetPermission) {
695                CharSequence alwaysAllowBindAppWidgetsText =
696                        getText(R.string.always_allow_bind_appwidgets_text);
697                SpannableString s = new SpannableString(alwaysAllowBindAppWidgetsText);
698                if (useBullets) {
699                    s.setSpan(new BulletSpan(bulletIndent),
700                            0, alwaysAllowBindAppWidgetsText.length(), 0);
701                }
702                text = (text == null) ?
703                        TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n");
704            }
705            autoLaunchView.setText(text);
706            mActivitiesButton.setEnabled(true);
707            mActivitiesButton.setOnClickListener(this);
708        }
709
710        // Screen compatibility section.
711        ActivityManager am = (ActivityManager)
712                getActivity().getSystemService(Context.ACTIVITY_SERVICE);
713        int compatMode = am.getPackageScreenCompatMode(packageName);
714        // For now these are always off; this is the old UI model which we
715        // are no longer using.
716        if (false && (compatMode == ActivityManager.COMPAT_MODE_DISABLED
717                || compatMode == ActivityManager.COMPAT_MODE_ENABLED)) {
718            mScreenCompatSection.setVisibility(View.VISIBLE);
719            mAskCompatibilityCB.setChecked(am.getPackageAskScreenCompat(packageName));
720            mAskCompatibilityCB.setOnCheckedChangeListener(this);
721            mEnableCompatibilityCB.setChecked(compatMode == ActivityManager.COMPAT_MODE_ENABLED);
722            mEnableCompatibilityCB.setOnCheckedChangeListener(this);
723        } else {
724            mScreenCompatSection.setVisibility(View.GONE);
725        }
726
727        // Security permissions section
728        LinearLayout permsView = (LinearLayout) mRootView.findViewById(R.id.permissions_section);
729        AppSecurityPermissions asp = new AppSecurityPermissions(getActivity(), packageName);
730        int premiumSmsPermission = getPremiumSmsPermission(packageName);
731        // Premium SMS permission implies the app also has SEND_SMS permission, so the original
732        // application permissions list doesn't have to be shown/hidden separately. The premium
733        // SMS subsection should only be visible if the app has tried to send to a premium SMS.
734        if (asp.getPermissionCount() > 0
735                || premiumSmsPermission != SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) {
736            permsView.setVisibility(View.VISIBLE);
737        } else {
738            permsView.setVisibility(View.GONE);
739        }
740        // Premium SMS permission subsection
741        TextView securityBillingDesc = (TextView) permsView.findViewById(
742                R.id.security_settings_billing_desc);
743        LinearLayout securityBillingList = (LinearLayout) permsView.findViewById(
744                R.id.security_settings_billing_list);
745        if (premiumSmsPermission != SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) {
746            // Show the premium SMS permission selector
747            securityBillingDesc.setVisibility(View.VISIBLE);
748            securityBillingList.setVisibility(View.VISIBLE);
749            Spinner spinner = (Spinner) permsView.findViewById(
750                    R.id.security_settings_premium_sms_list);
751            ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(getActivity(),
752                    R.array.security_settings_premium_sms_values,
753                    android.R.layout.simple_spinner_item);
754            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
755            spinner.setAdapter(adapter);
756            // List items are in the same order as SmsUsageMonitor constants, offset by 1.
757            spinner.setSelection(premiumSmsPermission - 1);
758            spinner.setOnItemSelectedListener(new PremiumSmsSelectionListener(
759                    packageName, mSmsManager));
760        } else {
761            // Hide the premium SMS permission selector
762            securityBillingDesc.setVisibility(View.GONE);
763            securityBillingList.setVisibility(View.GONE);
764        }
765        // App permissions subsection
766        if (asp.getPermissionCount() > 0) {
767            // Make the security sections header visible
768            LinearLayout securityList = (LinearLayout) permsView.findViewById(
769                    R.id.security_settings_list);
770            securityList.removeAllViews();
771            securityList.addView(asp.getPermissionsViewWithRevokeButtons());
772            // If this app is running under a shared user ID with other apps,
773            // update the description to explain this.
774            String[] packages = mPm.getPackagesForUid(mPackageInfo.applicationInfo.uid);
775            if (packages != null && packages.length > 1) {
776                ArrayList<CharSequence> pnames = new ArrayList<CharSequence>();
777                for (int i=0; i<packages.length; i++) {
778                    String pkg = packages[i];
779                    if (mPackageInfo.packageName.equals(pkg)) {
780                        continue;
781                    }
782                    try {
783                        ApplicationInfo ainfo = mPm.getApplicationInfo(pkg, 0);
784                        pnames.add(ainfo.loadLabel(mPm));
785                    } catch (PackageManager.NameNotFoundException e) {
786                    }
787                }
788                final int N = pnames.size();
789                if (N > 0) {
790                    final Resources res = getActivity().getResources();
791                    String appListStr;
792                    if (N == 1) {
793                        appListStr = pnames.get(0).toString();
794                    } else if (N == 2) {
795                        appListStr = res.getString(R.string.join_two_items, pnames.get(0),
796                                pnames.get(1));
797                    } else {
798                        appListStr = pnames.get(N-2).toString();
799                        for (int i=N-3; i>=0; i--) {
800                            appListStr = res.getString(i == 0 ? R.string.join_many_items_first
801                                    : R.string.join_many_items_middle, pnames.get(i), appListStr);
802                        }
803                        appListStr = res.getString(R.string.join_many_items_last,
804                                appListStr, pnames.get(N-1));
805                    }
806                    TextView descr = (TextView) mRootView.findViewById(
807                            R.id.security_settings_desc);
808                    descr.setText(res.getString(R.string.security_settings_desc_multi,
809                            mPackageInfo.applicationInfo.loadLabel(mPm), appListStr));
810                }
811            }
812        }
813
814        checkForceStop();
815        setAppLabelAndIcon(mPackageInfo);
816        refreshButtons();
817        refreshSizeInfo();
818
819        if (!mInitialized) {
820            // First time init: are we displaying an uninstalled app?
821            mInitialized = true;
822            mShowUninstalled = (mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0;
823        } else {
824            // All other times: if the app no longer exists then we want
825            // to go away.
826            try {
827                ApplicationInfo ainfo = getActivity().getPackageManager().getApplicationInfo(
828                        mAppEntry.info.packageName, PackageManager.GET_UNINSTALLED_PACKAGES
829                        | PackageManager.GET_DISABLED_COMPONENTS);
830                if (!mShowUninstalled) {
831                    // If we did not start out with the app uninstalled, then
832                    // it transitioning to the uninstalled state for the current
833                    // user means we should go away as well.
834                    return (ainfo.flags&ApplicationInfo.FLAG_INSTALLED) != 0;
835                }
836            } catch (NameNotFoundException e) {
837                return false;
838            }
839        }
840
841        return true;
842    }
843
844    private static class PremiumSmsSelectionListener implements AdapterView.OnItemSelectedListener {
845        private final String mPackageName;
846        private final ISms mSmsManager;
847
848        PremiumSmsSelectionListener(String packageName, ISms smsManager) {
849            mPackageName = packageName;
850            mSmsManager = smsManager;
851        }
852
853        @Override
854        public void onItemSelected(AdapterView<?> parent, View view, int position,
855                long id) {
856            if (position >= 0 && position < 3) {
857                Log.d(TAG, "Selected premium SMS policy " + position);
858                setPremiumSmsPermission(mPackageName, (position + 1));
859            } else {
860                Log.e(TAG, "Error: unknown premium SMS policy " + position);
861            }
862        }
863
864        @Override
865        public void onNothingSelected(AdapterView<?> parent) {
866            // Ignored
867        }
868
869        private void setPremiumSmsPermission(String packageName, int permission) {
870            try {
871                if (mSmsManager != null) {
872                    mSmsManager.setPremiumSmsPermission(packageName, permission);
873                }
874            } catch (RemoteException ex) {
875                // ignored
876            }
877        }
878    }
879
880    private void resetLaunchDefaultsUi(TextView title, TextView autoLaunchView) {
881        title.setText(R.string.auto_launch_label);
882        autoLaunchView.setText(R.string.auto_launch_disable_text);
883        // Disable clear activities button
884        mActivitiesButton.setEnabled(false);
885    }
886
887    private void setIntentAndFinish(boolean finish, boolean appChanged) {
888        if(localLOGV) Log.i(TAG, "appChanged="+appChanged);
889        Intent intent = new Intent();
890        intent.putExtra(ManageApplications.APP_CHG, appChanged);
891        PreferenceActivity pa = (PreferenceActivity)getActivity();
892        pa.finishPreferencePanel(this, Activity.RESULT_OK, intent);
893    }
894
895    private void refreshSizeInfo() {
896        if (mAppEntry.size == ApplicationsState.SIZE_INVALID
897                || mAppEntry.size == ApplicationsState.SIZE_UNKNOWN) {
898            mLastCodeSize = mLastDataSize = mLastCacheSize = mLastTotalSize = -1;
899            if (!mHaveSizes) {
900                mAppSize.setText(mComputingStr);
901                mDataSize.setText(mComputingStr);
902                mCacheSize.setText(mComputingStr);
903                mTotalSize.setText(mComputingStr);
904            }
905            mClearDataButton.setEnabled(false);
906            mClearCacheButton.setEnabled(false);
907
908        } else {
909            mHaveSizes = true;
910            long codeSize = mAppEntry.codeSize;
911            long dataSize = mAppEntry.dataSize;
912            if (Environment.isExternalStorageEmulated()) {
913                codeSize += mAppEntry.externalCodeSize;
914                dataSize +=  mAppEntry.externalDataSize;
915            } else {
916                if (mLastExternalCodeSize != mAppEntry.externalCodeSize) {
917                    mLastExternalCodeSize = mAppEntry.externalCodeSize;
918                    mExternalCodeSize.setText(getSizeStr(mAppEntry.externalCodeSize));
919                }
920                if (mLastExternalDataSize !=  mAppEntry.externalDataSize) {
921                    mLastExternalDataSize =  mAppEntry.externalDataSize;
922                    mExternalDataSize.setText(getSizeStr( mAppEntry.externalDataSize));
923                }
924            }
925            if (mLastCodeSize != codeSize) {
926                mLastCodeSize = codeSize;
927                mAppSize.setText(getSizeStr(codeSize));
928            }
929            if (mLastDataSize != dataSize) {
930                mLastDataSize = dataSize;
931                mDataSize.setText(getSizeStr(dataSize));
932            }
933            long cacheSize = mAppEntry.cacheSize + mAppEntry.externalCacheSize;
934            if (mLastCacheSize != cacheSize) {
935                mLastCacheSize = cacheSize;
936                mCacheSize.setText(getSizeStr(cacheSize));
937            }
938            if (mLastTotalSize != mAppEntry.size) {
939                mLastTotalSize = mAppEntry.size;
940                mTotalSize.setText(getSizeStr(mAppEntry.size));
941            }
942
943            if ((mAppEntry.dataSize+ mAppEntry.externalDataSize) <= 0 || !mCanClearData) {
944                mClearDataButton.setEnabled(false);
945            } else {
946                mClearDataButton.setEnabled(true);
947                mClearDataButton.setOnClickListener(this);
948            }
949            if (cacheSize <= 0) {
950                mClearCacheButton.setEnabled(false);
951            } else {
952                mClearCacheButton.setEnabled(true);
953                mClearCacheButton.setOnClickListener(this);
954            }
955        }
956    }
957
958    /*
959     * Private method to handle clear message notification from observer when
960     * the async operation from PackageManager is complete
961     */
962    private void processClearMsg(Message msg) {
963        int result = msg.arg1;
964        String packageName = mAppEntry.info.packageName;
965        mClearDataButton.setText(R.string.clear_user_data_text);
966        if(result == OP_SUCCESSFUL) {
967            Log.i(TAG, "Cleared user data for package : "+packageName);
968            mState.requestSize(mAppEntry.info.packageName);
969        } else {
970            mClearDataButton.setEnabled(true);
971        }
972        checkForceStop();
973    }
974
975    private void refreshButtons() {
976        if (!mMoveInProgress) {
977            initUninstallButtons();
978            initDataButtons();
979            initMoveButton();
980            initNotificationButton();
981        } else {
982            mMoveAppButton.setText(R.string.moving);
983            mMoveAppButton.setEnabled(false);
984            mUninstallButton.setEnabled(false);
985            mSpecialDisableButton.setEnabled(false);
986        }
987    }
988
989    private void processMoveMsg(Message msg) {
990        int result = msg.arg1;
991        String packageName = mAppEntry.info.packageName;
992        // Refresh the button attributes.
993        mMoveInProgress = false;
994        if (result == PackageManager.MOVE_SUCCEEDED) {
995            Log.i(TAG, "Moved resources for " + packageName);
996            // Refresh size information again.
997            mState.requestSize(mAppEntry.info.packageName);
998        } else {
999            showDialogInner(DLG_MOVE_FAILED, result);
1000        }
1001        refreshUi();
1002    }
1003
1004    /*
1005     * Private method to initiate clearing user data when the user clicks the clear data
1006     * button for a system package
1007     */
1008    private  void initiateClearUserData() {
1009        mClearDataButton.setEnabled(false);
1010        // Invoke uninstall or clear user data based on sysPackage
1011        String packageName = mAppEntry.info.packageName;
1012        Log.i(TAG, "Clearing user data for package : " + packageName);
1013        if (mClearDataObserver == null) {
1014            mClearDataObserver = new ClearUserDataObserver();
1015        }
1016        ActivityManager am = (ActivityManager)
1017                getActivity().getSystemService(Context.ACTIVITY_SERVICE);
1018        boolean res = am.clearApplicationUserData(packageName, mClearDataObserver);
1019        if (!res) {
1020            // Clearing data failed for some obscure reason. Just log error for now
1021            Log.i(TAG, "Couldnt clear application user data for package:"+packageName);
1022            showDialogInner(DLG_CANNOT_CLEAR_DATA, 0);
1023        } else {
1024            mClearDataButton.setText(R.string.recompute_size);
1025        }
1026    }
1027
1028    private void showDialogInner(int id, int moveErrorCode) {
1029        DialogFragment newFragment = MyAlertDialogFragment.newInstance(id, moveErrorCode);
1030        newFragment.setTargetFragment(this, 0);
1031        newFragment.show(getFragmentManager(), "dialog " + id);
1032    }
1033
1034    public static class MyAlertDialogFragment extends DialogFragment {
1035
1036        public static MyAlertDialogFragment newInstance(int id, int moveErrorCode) {
1037            MyAlertDialogFragment frag = new MyAlertDialogFragment();
1038            Bundle args = new Bundle();
1039            args.putInt("id", id);
1040            args.putInt("moveError", moveErrorCode);
1041            frag.setArguments(args);
1042            return frag;
1043        }
1044
1045        InstalledAppDetails getOwner() {
1046            return (InstalledAppDetails)getTargetFragment();
1047        }
1048
1049        @Override
1050        public Dialog onCreateDialog(Bundle savedInstanceState) {
1051            int id = getArguments().getInt("id");
1052            int moveErrorCode = getArguments().getInt("moveError");
1053            switch (id) {
1054                case DLG_CLEAR_DATA:
1055                    return new AlertDialog.Builder(getActivity())
1056                    .setTitle(getActivity().getText(R.string.clear_data_dlg_title))
1057                    .setIconAttribute(android.R.attr.alertDialogIcon)
1058                    .setMessage(getActivity().getText(R.string.clear_data_dlg_text))
1059                    .setPositiveButton(R.string.dlg_ok,
1060                            new DialogInterface.OnClickListener() {
1061                        public void onClick(DialogInterface dialog, int which) {
1062                            // Clear user data here
1063                            getOwner().initiateClearUserData();
1064                        }
1065                    })
1066                    .setNegativeButton(R.string.dlg_cancel, null)
1067                    .create();
1068                case DLG_FACTORY_RESET:
1069                    return new AlertDialog.Builder(getActivity())
1070                    .setTitle(getActivity().getText(R.string.app_factory_reset_dlg_title))
1071                    .setIconAttribute(android.R.attr.alertDialogIcon)
1072                    .setMessage(getActivity().getText(R.string.app_factory_reset_dlg_text))
1073                    .setPositiveButton(R.string.dlg_ok,
1074                            new DialogInterface.OnClickListener() {
1075                        public void onClick(DialogInterface dialog, int which) {
1076                            // Clear user data here
1077                            getOwner().uninstallPkg(getOwner().mAppEntry.info.packageName,
1078                                    false, false);
1079                        }
1080                    })
1081                    .setNegativeButton(R.string.dlg_cancel, null)
1082                    .create();
1083                case DLG_APP_NOT_FOUND:
1084                    return new AlertDialog.Builder(getActivity())
1085                    .setTitle(getActivity().getText(R.string.app_not_found_dlg_title))
1086                    .setIconAttribute(android.R.attr.alertDialogIcon)
1087                    .setMessage(getActivity().getText(R.string.app_not_found_dlg_title))
1088                    .setNeutralButton(getActivity().getText(R.string.dlg_ok),
1089                            new DialogInterface.OnClickListener() {
1090                        public void onClick(DialogInterface dialog, int which) {
1091                            //force to recompute changed value
1092                            getOwner().setIntentAndFinish(true, true);
1093                        }
1094                    })
1095                    .create();
1096                case DLG_CANNOT_CLEAR_DATA:
1097                    return new AlertDialog.Builder(getActivity())
1098                    .setTitle(getActivity().getText(R.string.clear_failed_dlg_title))
1099                    .setIconAttribute(android.R.attr.alertDialogIcon)
1100                    .setMessage(getActivity().getText(R.string.clear_failed_dlg_text))
1101                    .setNeutralButton(R.string.dlg_ok,
1102                            new DialogInterface.OnClickListener() {
1103                        public void onClick(DialogInterface dialog, int which) {
1104                            getOwner().mClearDataButton.setEnabled(false);
1105                            //force to recompute changed value
1106                            getOwner().setIntentAndFinish(false, false);
1107                        }
1108                    })
1109                    .create();
1110                case DLG_FORCE_STOP:
1111                    return new AlertDialog.Builder(getActivity())
1112                    .setTitle(getActivity().getText(R.string.force_stop_dlg_title))
1113                    .setIconAttribute(android.R.attr.alertDialogIcon)
1114                    .setMessage(getActivity().getText(R.string.force_stop_dlg_text))
1115                    .setPositiveButton(R.string.dlg_ok,
1116                        new DialogInterface.OnClickListener() {
1117                        public void onClick(DialogInterface dialog, int which) {
1118                            // Force stop
1119                            getOwner().forceStopPackage(getOwner().mAppEntry.info.packageName);
1120                        }
1121                    })
1122                    .setNegativeButton(R.string.dlg_cancel, null)
1123                    .create();
1124                case DLG_MOVE_FAILED:
1125                    CharSequence msg = getActivity().getString(R.string.move_app_failed_dlg_text,
1126                            getOwner().getMoveErrMsg(moveErrorCode));
1127                    return new AlertDialog.Builder(getActivity())
1128                    .setTitle(getActivity().getText(R.string.move_app_failed_dlg_title))
1129                    .setIconAttribute(android.R.attr.alertDialogIcon)
1130                    .setMessage(msg)
1131                    .setNeutralButton(R.string.dlg_ok, null)
1132                    .create();
1133                case DLG_DISABLE:
1134                    return new AlertDialog.Builder(getActivity())
1135                    .setTitle(getActivity().getText(R.string.app_disable_dlg_title))
1136                    .setIconAttribute(android.R.attr.alertDialogIcon)
1137                    .setMessage(getActivity().getText(R.string.app_disable_dlg_text))
1138                    .setPositiveButton(R.string.dlg_ok,
1139                        new DialogInterface.OnClickListener() {
1140                        public void onClick(DialogInterface dialog, int which) {
1141                            // Disable the app
1142                            new DisableChanger(getOwner(), getOwner().mAppEntry.info,
1143                                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)
1144                            .execute((Object)null);
1145                        }
1146                    })
1147                    .setNegativeButton(R.string.dlg_cancel, null)
1148                    .create();
1149                case DLG_DISABLE_NOTIFICATIONS:
1150                    return new AlertDialog.Builder(getActivity())
1151                    .setTitle(getActivity().getText(R.string.app_disable_notifications_dlg_title))
1152                    .setIconAttribute(android.R.attr.alertDialogIcon)
1153                    .setMessage(getActivity().getText(R.string.app_disable_notifications_dlg_text))
1154                    .setPositiveButton(R.string.dlg_ok,
1155                        new DialogInterface.OnClickListener() {
1156                        public void onClick(DialogInterface dialog, int which) {
1157                            // Disable the package's notifications
1158                            getOwner().setNotificationsEnabled(false);
1159                        }
1160                    })
1161                    .setNegativeButton(R.string.dlg_cancel,
1162                        new DialogInterface.OnClickListener() {
1163                        public void onClick(DialogInterface dialog, int which) {
1164                            // Re-enable the checkbox
1165                            getOwner().mNotificationSwitch.setChecked(true);
1166                        }
1167                    })
1168                    .create();
1169                case DLG_SPECIAL_DISABLE:
1170                    return new AlertDialog.Builder(getActivity())
1171                    .setTitle(getActivity().getText(R.string.app_special_disable_dlg_title))
1172                    .setIconAttribute(android.R.attr.alertDialogIcon)
1173                    .setMessage(getActivity().getText(R.string.app_special_disable_dlg_text))
1174                    .setPositiveButton(R.string.dlg_ok,
1175                            new DialogInterface.OnClickListener() {
1176                        public void onClick(DialogInterface dialog, int which) {
1177                            // Clear user data here
1178                            getOwner().uninstallPkg(getOwner().mAppEntry.info.packageName,
1179                                    false, true);
1180                        }
1181                    })
1182                    .setNegativeButton(R.string.dlg_cancel, null)
1183                    .create();
1184            }
1185            throw new IllegalArgumentException("unknown id " + id);
1186        }
1187    }
1188
1189    private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) {
1190         // Create new intent to launch Uninstaller activity
1191        Uri packageURI = Uri.parse("package:"+packageName);
1192        Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI);
1193        uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers);
1194        startActivityForResult(uninstallIntent, REQUEST_UNINSTALL);
1195        mDisableAfterUninstall = andDisable;
1196    }
1197
1198    private void forceStopPackage(String pkgName) {
1199        ActivityManager am = (ActivityManager)getActivity().getSystemService(
1200                Context.ACTIVITY_SERVICE);
1201        am.forceStopPackage(pkgName);
1202        mState.invalidatePackage(pkgName);
1203        ApplicationsState.AppEntry newEnt = mState.getEntry(pkgName);
1204        if (newEnt != null) {
1205            mAppEntry = newEnt;
1206        }
1207        checkForceStop();
1208    }
1209
1210    private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
1211        @Override
1212        public void onReceive(Context context, Intent intent) {
1213            updateForceStopButton(getResultCode() != Activity.RESULT_CANCELED);
1214        }
1215    };
1216
1217    private void updateForceStopButton(boolean enabled) {
1218        mForceStopButton.setEnabled(enabled);
1219        mForceStopButton.setOnClickListener(InstalledAppDetails.this);
1220    }
1221
1222    private void checkForceStop() {
1223        if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
1224            // User can't force stop device admin.
1225            updateForceStopButton(false);
1226        } else if ((mAppEntry.info.flags&ApplicationInfo.FLAG_STOPPED) == 0) {
1227            // If the app isn't explicitly stopped, then always show the
1228            // force stop button.
1229            updateForceStopButton(true);
1230        } else {
1231            Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
1232                    Uri.fromParts("package", mAppEntry.info.packageName, null));
1233            intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mAppEntry.info.packageName });
1234            intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid);
1235            intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(mAppEntry.info.uid));
1236            getActivity().sendOrderedBroadcast(intent, null, mCheckKillProcessesReceiver, null,
1237                    Activity.RESULT_CANCELED, null, null);
1238        }
1239    }
1240
1241    static class DisableChanger extends AsyncTask<Object, Object, Object> {
1242        final PackageManager mPm;
1243        final WeakReference<InstalledAppDetails> mActivity;
1244        final ApplicationInfo mInfo;
1245        final int mState;
1246
1247        DisableChanger(InstalledAppDetails activity, ApplicationInfo info, int state) {
1248            mPm = activity.mPm;
1249            mActivity = new WeakReference<InstalledAppDetails>(activity);
1250            mInfo = info;
1251            mState = state;
1252        }
1253
1254        @Override
1255        protected Object doInBackground(Object... params) {
1256            mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0);
1257            return null;
1258        }
1259    }
1260
1261    private void setNotificationsEnabled(boolean enabled) {
1262        String packageName = mAppEntry.info.packageName;
1263        INotificationManager nm = INotificationManager.Stub.asInterface(
1264                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
1265        try {
1266            final boolean enable = mNotificationSwitch.isChecked();
1267            nm.setNotificationsEnabledForPackage(packageName, mAppEntry.info.uid, enabled);
1268        } catch (android.os.RemoteException ex) {
1269            mNotificationSwitch.setChecked(!enabled); // revert
1270        }
1271    }
1272
1273    private int getPremiumSmsPermission(String packageName) {
1274        try {
1275            if (mSmsManager != null) {
1276                return mSmsManager.getPremiumSmsPermission(packageName);
1277            }
1278        } catch (RemoteException ex) {
1279            // ignored
1280        }
1281        return SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN;
1282    }
1283
1284    /*
1285     * Method implementing functionality of buttons clicked
1286     * @see android.view.View.OnClickListener#onClick(android.view.View)
1287     */
1288    public void onClick(View v) {
1289        String packageName = mAppEntry.info.packageName;
1290        if(v == mUninstallButton) {
1291            if (mUpdatedSysApp) {
1292                showDialogInner(DLG_FACTORY_RESET, 0);
1293            } else {
1294                if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
1295                    if (mAppEntry.info.enabled) {
1296                        showDialogInner(DLG_DISABLE, 0);
1297                    } else {
1298                        new DisableChanger(this, mAppEntry.info,
1299                                PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
1300                        .execute((Object)null);
1301                    }
1302                } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
1303                    uninstallPkg(packageName, true, false);
1304                } else {
1305                    uninstallPkg(packageName, false, false);
1306                }
1307            }
1308        } else if(v == mSpecialDisableButton) {
1309            showDialogInner(DLG_SPECIAL_DISABLE, 0);
1310        } else if(v == mActivitiesButton) {
1311            mPm.clearPackagePreferredActivities(packageName);
1312            try {
1313                mUsbManager.clearDefaults(packageName, UserHandle.myUserId());
1314            } catch (RemoteException e) {
1315                Log.e(TAG, "mUsbManager.clearDefaults", e);
1316            }
1317            mAppWidgetManager.setBindAppWidgetPermission(packageName, false);
1318            TextView autoLaunchTitleView =
1319                    (TextView) mRootView.findViewById(R.id.auto_launch_title);
1320            TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch);
1321            resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView);
1322        } else if(v == mClearDataButton) {
1323            if (mAppEntry.info.manageSpaceActivityName != null) {
1324                if (!Utils.isMonkeyRunning()) {
1325                    Intent intent = new Intent(Intent.ACTION_DEFAULT);
1326                    intent.setClassName(mAppEntry.info.packageName,
1327                            mAppEntry.info.manageSpaceActivityName);
1328                    startActivityForResult(intent, REQUEST_MANAGE_SPACE);
1329                }
1330            } else {
1331                showDialogInner(DLG_CLEAR_DATA, 0);
1332            }
1333        } else if (v == mClearCacheButton) {
1334            // Lazy initialization of observer
1335            if (mClearCacheObserver == null) {
1336                mClearCacheObserver = new ClearCacheObserver();
1337            }
1338            mPm.deleteApplicationCacheFiles(packageName, mClearCacheObserver);
1339        } else if (v == mForceStopButton) {
1340            showDialogInner(DLG_FORCE_STOP, 0);
1341            //forceStopPackage(mAppInfo.packageName);
1342        } else if (v == mMoveAppButton) {
1343            if (mPackageMoveObserver == null) {
1344                mPackageMoveObserver = new PackageMoveObserver();
1345            }
1346            int moveFlags = (mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0 ?
1347                    PackageManager.MOVE_INTERNAL : PackageManager.MOVE_EXTERNAL_MEDIA;
1348            mMoveInProgress = true;
1349            refreshButtons();
1350            mPm.movePackage(mAppEntry.info.packageName, mPackageMoveObserver, moveFlags);
1351        }
1352    }
1353
1354    @Override
1355    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
1356        String packageName = mAppEntry.info.packageName;
1357        ActivityManager am = (ActivityManager)
1358                getActivity().getSystemService(Context.ACTIVITY_SERVICE);
1359        if (buttonView == mAskCompatibilityCB) {
1360            am.setPackageAskScreenCompat(packageName, isChecked);
1361        } else if (buttonView == mEnableCompatibilityCB) {
1362            am.setPackageScreenCompatMode(packageName, isChecked ?
1363                    ActivityManager.COMPAT_MODE_ENABLED : ActivityManager.COMPAT_MODE_DISABLED);
1364        } else if (buttonView == mNotificationSwitch) {
1365            if (!isChecked) {
1366                showDialogInner(DLG_DISABLE_NOTIFICATIONS, 0);
1367            } else {
1368                setNotificationsEnabled(true);
1369            }
1370        }
1371    }
1372}
1373
1374