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