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