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